home *** CD-ROM | disk | FTP | other *** search
- /*
- * bltHtext.c --
- *
- * This module implements a hypertext widget for the
- * Tk toolkit.
- *
- * Copyright 1991-1994 by AT&T Bell Laboratories.
- * Permission to use, copy, modify, and distribute this software
- * and its documentation for any purpose and without fee is hereby
- * granted, provided that the above copyright notice appear in all
- * copies and that both that the copyright notice and warranty
- * disclaimer appear in supporting documentation, and that the
- * names of AT&T Bell Laboratories any of their entities not be used
- * in advertising or publicity pertaining to distribution of the
- * software without specific, written prior permission.
- *
- * AT&T disclaims all warranties with regard to this software, including
- * all implied warranties of merchantability and fitness. In no event
- * shall AT&T be liable for any special, indirect or consequential
- * damages or any damages whatsoever resulting from loss of use, data
- * or profits, whether in an action of contract, negligence or other
- * tortious action, arising out of or in connection with the use or
- * performance of this software.
- *
- * Hypertext widget created by George Howlett.
- */
-
- /*
- * To do:
- *
- * 1) Fix scroll unit round off errors.
- *
- * 2) Bug in reporting errors in Tcl evaluations.
- *
- * 3) Selections of text. (characters, word, line)
- *
- * 4) Tabstops for easier placement of text and child widgets.
- * Use variable "tabstops" to set/reset tabstops.
- *
- * 5) Better error checking.
- *
- */
-
- #include "blt.h"
- #include <X11/Xutil.h>
-
- #if (TK_MINOR_VERSION > 0)
- #define Cursor Tk_Cursor
- #endif
-
- #ifndef HTEXT_VERSION
- #define HTEXT_VERSION "2.4"
- #endif
-
- #define HTEXT_CMDNAME "blt_htext"
-
- #define LINES_ALLOC_CHUNK 512
-
- #define BLT_MIN(a,b) (((a)<(b))?(a):(b))
- #define BLT_MAX(a,b) (((a)>(b))?(a):(b))
-
- /*
- * Flags passed to TkMeasureChars: taken from tkInt.h
- */
- #define TK_WHOLE_WORDS 1
- #define TK_AT_LEAST_ONE 2
- #define TK_PARTIAL_OK 4
-
- /*
- * The following enumerated values are used as bit flags.
- */
- typedef enum {
- FILL_NONE, FILL_X, FILL_Y, FILL_BOTH
- } FillFlags;
-
- static int ParseFill _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
- Tk_Window tkwin, char *value, char *widgRec, int offset));
- static char *PrintFill _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
- char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
-
- static Tk_CustomOption FillOption =
- {
- ParseFill, PrintFill, (ClientData)0
- };
-
- /*
- * Justify option values
- */
- typedef enum {
- JUSTIFY_CENTER, JUSTIFY_TOP, JUSTIFY_BOTTOM
- } Justify;
-
- static int ParseJustify _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *value, char *widgRec, int offset));
- static char *PrintJustify _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
-
- static Tk_CustomOption JustifyOption =
- {
- ParseJustify, PrintJustify, (ClientData)0
- };
-
- static char *fillStrings[] =
- {
- "none", "x", "y", "both"
- };
- static char *justifyStrings[] =
- {
- "center", "top", "bottom"
- };
-
- typedef struct Child {
- struct Htext *textPtr; /* Pointer to parent's Htext structure */
- Tk_Window tkwin; /* Widget window */
- int flags;
-
- int x, y; /* Origin of child subwindow in text */
-
- /* Dimensions of the cavity surrounding the child window */
-
- unsigned int cavityWidth, cavityHeight;
-
- /*
- * Dimensions of the child window. Saved for later comparisons to
- * check for resizing.
- */
- unsigned int windowWidth, windowHeight;
-
- int precedingTextEnd; /* Number of characters of text */
- int precedingTextWidth; /* Width of normal text preceding child */
-
- Tk_Anchor anchor;
- Justify justify; /* Justification of region wrt to line */
-
- int reqWidth, reqHeight; /* Requested dimension of cavity */
- double relWidth, relHeight; /* Relative dimensions of cavity.
- * Sizes are calculated with respect
- * the size of the viewport */
- int padX, padY; /* Extra padding to frame around */
- int ipadX, ipadY; /* Internal padding for window */
-
- FillFlags fill; /* Fill style flag */
-
- } Child;
-
- /*
- * Flag bits children subwindows:
- */
- #define VISIBLE 4 /* Subwindow is visible in the viewport. */
-
- /*
- * Information used for parsing configuration specs:
- */
-
- /*
- * Defaults for children:
- */
-
- #define DEF_CHILD_ANCHOR "center"
- #define DEF_CHILD_FILL "none"
- #define DEF_CHILD_HEIGHT "0"
- #define DEF_CHILD_JUSTIFY "center"
- #define DEF_CHILD_PAD_X "0"
- #define DEF_CHILD_PAD_Y "0"
- #define DEF_CHILD_REL_HEIGHT "0.0"
- #define DEF_CHILD_REL_WIDTH "0.0"
- #define DEF_CHILD_WIDTH "0"
-
- static Tk_ConfigSpec childConfigSpecs[] =
- {
- {TK_CONFIG_ANCHOR, "-anchor", (char *)NULL, (char *)NULL,
- DEF_CHILD_ANCHOR, Tk_Offset(Child, anchor),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_CUSTOM, "-fill", (char *)NULL, (char *)NULL,
- DEF_CHILD_FILL, Tk_Offset(Child, fill),
- TK_CONFIG_DONT_SET_DEFAULT, &FillOption},
- {TK_CONFIG_PIXELS, "-height", (char *)NULL, (char *)NULL,
- DEF_CHILD_HEIGHT, Tk_Offset(Child, reqHeight),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_CUSTOM, "-justify", (char *)NULL, (char *)NULL,
- DEF_CHILD_JUSTIFY, Tk_Offset(Child, justify),
- TK_CONFIG_DONT_SET_DEFAULT, &JustifyOption},
- {TK_CONFIG_PIXELS, "-padx", (char *)NULL, (char *)NULL,
- DEF_CHILD_PAD_X, Tk_Offset(Child, padX),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_PIXELS, "-pady", (char *)NULL, (char *)NULL,
- DEF_CHILD_PAD_Y, Tk_Offset(Child, padY),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_DOUBLE, "-relheight", (char *)NULL, (char *)NULL,
- DEF_CHILD_REL_HEIGHT, Tk_Offset(Child, relHeight),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_DOUBLE, "-relwidth", (char *)NULL, (char *)NULL,
- DEF_CHILD_REL_WIDTH, Tk_Offset(Child, relWidth),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_PIXELS, "-width", (char *)NULL, (char *)NULL,
- DEF_CHILD_WIDTH, Tk_Offset(Child, reqWidth),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
- (char *)NULL, 0, 0}
- };
-
- /*
- * Structure to contain the contents of a single line of text and the
- * children on that line.
- *
- * Individual lines are not configurable, although changes to the size
- * of children do effect its values.
- */
-
- typedef struct {
- int offset; /* Pixel offset from world coordinate 0,0 */
-
- short int height; /* Height of line */
- short int width; /* Width of line */
- short int baseline; /* Baseline of text */
- short int numChars; /* Number of characters in normal text */
- char *text; /* The plain text on the line */
-
- Blt_LinkedList windowList; /* List of subwindows on the line of text */
-
- } Line;
-
-
- typedef struct {
- Tk_3DBorder border; /* Border and background for selected
- * characters. */
- int borderWidth; /* Width of border around selection. */
- XColor *activeFg; /* Foreground color for selected text. */
- GC gc; /* For drawing selected text. */
-
- int first; /* Position of first selected character
- * (-1 means nothing is selected). */
- int last; /* Position of last selected character.
- * (-1 means nothing is selected). */
- int anchor; /* Fixed end of selection
- * (i.e. "select to" operation will
- * use this as one end of the selection).*/
- } Selection;
-
- typedef struct {
- long int x, y;
- long int width, height;
- } ViewPort;
-
- /*
- * Hypertext widget.
- */
- typedef struct Htext {
- Tk_Window tkwin; /* Window that embodies the widget.
- * NULL means that the window has been
- * destroyed but the data structures
- * haven't yet been cleaned up.*/
- Display *display; /* Display containing widget; needed,
- * among other things, to release
- * resources after tkwin has already
- * gone away. */
- Tcl_Interp *interp; /* Interpreter associated with widget. */
- unsigned int flags;
-
- /* User-configurable fields */
-
- Tk_3DBorder border; /* Don't current use borders (only color) */
- XColor *normalFg;
- int reqLineNum; /* Line requested by "gotoline" command */
- int reqWidth, reqHeight; /* Requested dimensions of the text window */
- int maxWidth, maxHeight;
- Cursor cursor; /* X Cursor for child */
-
- char *yScrollCmd; /* Name of vertical scrollbar to invoke */
- int scrollY; /* # of pixels per vert scroll */
- char *xScrollCmd; /* Name of horizontal scroll bar to invoke */
- int scrollX; /* # of pixels per horiz scroll */
- int lineSpacing; /* # of pixels between lines */
- int specChar; /* Special character designating a TCL
- * command block in a hypertext file. */
- XFontStruct *fontPtr; /* Font for normal text */
- char *fileName; /* Name of hypertext file */
- char *text; /* Text */
-
-
- /*
- * The view port is the width and height of the window and the
- * origin of the viewport (upper left corner) in world coordinates.
- */
- ViewPort vPort; /* Position and size of viewport in
- * virtual coordinates */
- int pendingX, pendingY; /* New upper-left corner (origin) of
- * the viewport (not yet posted) */
-
- int first, last; /* Range of lines displayed */
-
- Tcl_HashTable subwindows; /* Table of child sub-windows */
- GC gc; /* Graphics context for normal text */
-
- #ifdef notdef
- Selection selection;
- #endif
- /*
- * Scanning information:
- */
- XPoint scanMark; /* Anchor position of scan */
- XPoint scanPt; /* x,y position where the scan started. */
-
- unsigned int width, height; /* Size of the window: saved to recognize
- * when the viewport is resized. */
- char *intBuffer; /* Internal text buffer */
- Line **linePtrPtr; /* Array of text lines */
- unsigned int numLines; /* # of line entered into array. */
- unsigned int arraySize; /* Size of array allocated. */
-
- int relief; /* Theoretically, the relief. Does nothing
- * but satisfy SpecTcl currently */
- } Htext;
-
- /*
- * Bit flags for the hypertext widget:
- */
- #define REDRAW_PENDING 1 /* A DoWhenIdle handler has already
- * been queued to redraw the window */
- #define IGNORE_EXPOSURES (1<<1) /* Ignore exposure events in the text
- * window. Potentially many expose
- * events can occur while rearranging
- * subwindows during a single call to
- * the DisplayText. */
- #define VIEW_RESIZED (1<<2) /* Size of viewport (window) has
- changed */
- #define VIEW_MOVED (1<<3) /* Position of viewport has moved.
- * This occurs when scrolling or
- * goto-ing a new line */
- #define REQUEST_LAYOUT (1<<4) /* Something has happened which
- * requires the layout of text and
- * child window positions to be
- * recalculated. The following
- * actions may cause this:
- *
- * 1) the contents of the hypertext
- * has changed by either the -file or
- * -text options.
- *
- * 2) a text attribute has changed
- * (line spacing, font, etc)
- *
- * 3) a subwindow has been resized or
- * moved.
- *
- * 4) a child configuration option has
- * changed.
- */
- #define LAYOUT_CHANGED (1<<5) /* The layout was recalculated and the
- * size of the world (text layout) has
- * changed. */
- #define REQUEST_GOTO (1<<6) /* Indicates the starting text line
- * number has changed. To be reflected
- * the next time the widget is
- * redrawn. */
-
- #define DEF_HTEXT_BG_COLOR BG2
- #define DEF_HTEXT_BG_MONO WHITE
- #define DEF_HTEXT_CURSOR "pencil"
- #define DEF_HTEXT_FG_COLOR BLACK
- #define DEF_HTEXT_FG_MONO BLACK
- #define DEF_HTEXT_FILE_NAME (char *)NULL
- #define DEF_HTEXT_FONT "-Adobe-Helvetica-Bold-R-Normal--*-120-*-*-*-*-*-*"
- #define DEF_HTEXT_HEIGHT "0"
- #define DEF_HTEXT_LINE_SPACING "1"
- #define DEF_HTEXT_MAX_HEIGHT "9.5i"
- #define DEF_HTEXT_MAX_WIDTH "6.5i"
- #define DEF_HTEXT_RELIEF "flat"
- #define DEF_HTEXT_SCROLL_UNITS "10"
- #define DEF_HTEXT_SPEC_CHAR "0x25"
- #define DEF_HTEXT_TEXT (char *)NULL
- #define DEF_HTEXT_WIDTH "0"
-
- static Tk_ConfigSpec configSpecs[] =
- {
- {TK_CONFIG_BORDER, "-background", "background", "Background",
- DEF_HTEXT_BG_COLOR, Tk_Offset(Htext, border),
- TK_CONFIG_COLOR_ONLY},
- {TK_CONFIG_BORDER, "-background", "background", "Background",
- DEF_HTEXT_BG_MONO, Tk_Offset(Htext, border), TK_CONFIG_MONO_ONLY},
- {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
- {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
- DEF_HTEXT_CURSOR, Tk_Offset(Htext, cursor), TK_CONFIG_NULL_OK},
- {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
- {TK_CONFIG_STRING, "-filename", "fileName", "FileName",
- DEF_HTEXT_FILE_NAME, Tk_Offset(Htext, fileName), TK_CONFIG_NULL_OK},
- {TK_CONFIG_FONT, "-font", "font", "Font",
- DEF_HTEXT_FONT, Tk_Offset(Htext, fontPtr), 0},
- {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
- DEF_HTEXT_FG_COLOR, Tk_Offset(Htext, normalFg), TK_CONFIG_COLOR_ONLY},
- {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
- DEF_HTEXT_FG_MONO, Tk_Offset(Htext, normalFg), TK_CONFIG_MONO_ONLY},
- {TK_CONFIG_PIXELS, "-height", "height", "Height",
- DEF_HTEXT_HEIGHT, Tk_Offset(Htext, reqHeight),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_PIXELS, "-linespacing", "lineSpacing", "LineSpacing",
- DEF_HTEXT_LINE_SPACING, Tk_Offset(Htext, lineSpacing),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_PIXELS, "-maxheight", "maxHeight", "MaxHeight",
- DEF_HTEXT_MAX_HEIGHT, Tk_Offset(Htext, maxHeight), 0},
- {TK_CONFIG_PIXELS, "-maxwidth", "maxWidth", "MaxWidth",
- DEF_HTEXT_MAX_WIDTH, Tk_Offset(Htext, maxWidth), 0},
- {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
- DEF_HTEXT_RELIEF, Tk_Offset(Htext, relief), 0},
- {TK_CONFIG_INT, "-specialchar", "specialChar", "SpecialChar",
- DEF_HTEXT_SPEC_CHAR, Tk_Offset(Htext, specChar), 0},
- {TK_CONFIG_STRING, "-text", "text", "Text",
- DEF_HTEXT_TEXT, Tk_Offset(Htext, text), TK_CONFIG_NULL_OK},
- {TK_CONFIG_PIXELS, "-width", "width", "Width",
- DEF_HTEXT_WIDTH, Tk_Offset(Htext, reqWidth),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
- (char *)NULL, Tk_Offset(Htext, xScrollCmd), TK_CONFIG_NULL_OK},
- {TK_CONFIG_PIXELS, "-xscrollunits", "xScrollUnits", "ScrollUnits",
- DEF_HTEXT_SCROLL_UNITS, Tk_Offset(Htext, scrollX),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
- (char *)NULL, Tk_Offset(Htext, yScrollCmd), TK_CONFIG_NULL_OK},
- {TK_CONFIG_PIXELS, "-yscrollunits", "yScrollUnits", "yScrollUnits",
- DEF_HTEXT_SCROLL_UNITS, Tk_Offset(Htext, scrollY),
- TK_CONFIG_DONT_SET_DEFAULT},
- {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
- (char *)NULL, 0, 0}
- };
-
- /* Forward Declarations */
-
- static int TextWidgetCmd _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, int argc, char **argv));
- static void DestroyText _ANSI_ARGS_((ClientData clientdata));
- static int ConfigureHtext _ANSI_ARGS_((Tcl_Interp *interp,
- Htext *textPtr, int argc, char **argv, int flags));
- static void TextEventProc _ANSI_ARGS_((ClientData clientdata,
- XEvent *eventPtr));
- static void EventuallyRedraw _ANSI_ARGS_((Htext *textPtr));
- static int IncludeText _ANSI_ARGS_((Tcl_Interp *, Htext *, char *));
- static int ParseInput _ANSI_ARGS_((Tcl_Interp *, Htext *, char *));
- static char *ReadFile _ANSI_ARGS_((Tcl_Interp *interp, char *fileName));
- static int AppendChild _ANSI_ARGS_((Htext *, Tcl_Interp *, int, char **));
- static Child *CreateChild _ANSI_ARGS_((Htext *textPtr, char *pathName));
- static void DestroyChild _ANSI_ARGS_((Child *childPtr));
- static void ChildStructureProc _ANSI_ARGS_((ClientData clientdata,
- XEvent *eventPtr));
- static Child *FindChild _ANSI_ARGS_((Htext *textPtr, char *childName));
- static char *CollectCommand _ANSI_ARGS_((Htext *textPtr, char *inputPtr,
- char *command));
- static Line *NewLine _ANSI_ARGS_((Htext *textPtr));
- static void DestroyLine _ANSI_ARGS_((Line *linePtr));
- static Line *GetLastLine _ANSI_ARGS_((Htext *textPtr));
- static void SetTextInfo _ANSI_ARGS_((Line *linePtr, char *line, int size));
- static void GetTextInfo _ANSI_ARGS_((Line *linePtr, char *line, int *size));
- static void ComputeLayout _ANSI_ARGS_((Htext *textPtr));
- static void LayoutLine _ANSI_ARGS_((Htext *textPtr, Line *linePtr));
- static void FreeAllLines _ANSI_ARGS_((Htext *textPtr));
- static void AdjustLinesAllocated _ANSI_ARGS_((Htext *textPtr));
- static void DisplayText _ANSI_ARGS_((ClientData clientData));
- static void DrawPage _ANSI_ARGS_((Htext *textPtr, int deltaY));
- static void MoveChild _ANSI_ARGS_((Child *childPtr, int offset));
- static void UpdateScrollbar _ANSI_ARGS_((Tcl_Interp *interp, char *cmd,
- int total, int window, int first, int units));
- static int GetVisibleLines _ANSI_ARGS_((Htext *textPtr));
- static int LineSearch _ANSI_ARGS_((Htext *textPtr, int position,
- int low, int high));
- static int ResizeArray _ANSI_ARGS_((char **memPtr, unsigned int elemSize,
- unsigned int oldCount, unsigned int newCount));
- static void CreateTraces _ANSI_ARGS_((Htext *textPtr));
- static void DeleteTraces _ANSI_ARGS_((Htext *textPtr));
- static void ChildGeometryProc _ANSI_ARGS_((ClientData clientData,
- Tk_Window tkwin));
-
- static Tk_GeomMgr ChildGeometry = { "BltHtext", ChildGeometryProc, NULL } ;
-
- static void SendBogusEvent _ANSI_ARGS_((Tk_Window tkwin));
-
- extern void TkDisplayChars _ANSI_ARGS_((Display *display,
- Drawable drawable, GC gc,
- XFontStruct *fontStructPtr, char *string,
- int numChars, int x, int y, int tabOrigin,
- int flags));
- extern int TkMeasureChars _ANSI_ARGS_((XFontStruct *fontStructPtr,
- char *source, int maxChars, int startX, int maxX,
- int tabOrigin, int flags, int *nextXPtr));
-
- #if (TK_MINOR_VERSION > 0)
- extern int Blt_OptionChanged _ANSI_ARGS_(TCL_VARARGS(Tk_ConfigSpec *,specs));
- #else
- extern int Blt_OptionChanged _ANSI_ARGS_(VARARGS(Tk_ConfigSpec *specs));
- #endif
-
- /* end of Forward Declarations */
-
- /*
- *----------------------------------------------------------------------
- *
- * ParseFill --
- *
- * Converts the fill style string into its numeric representation.
- * This configuration option affects how the slave window is expanded
- * if there is extra space in the cubicle in which it sits.
- *
- * Valid style strings are:
- *
- * "none" Don't expand the window to fill the cubicle.
- * "x" Expand only the window's width.
- * "y" Expand only the window's height.
- * "both" Expand both the window's height and width.
- *
- *----------------------------------------------------------------------
- */
- /*ARGSUSED*/
- static int
- ParseFill(clientData, interp, tkwin, value, widgRec, offset)
- ClientData clientData; /* not used */
- Tcl_Interp *interp; /* Interpreter to send results back to */
- Tk_Window tkwin; /* not used */
- char *value; /* Fill style string */
- char *widgRec; /* Cubicle structure record */
- int offset; /* Offset of style in record */
- {
- FillFlags *fillPtr = (FillFlags *)(widgRec + offset);
- int length;
- char c;
-
- c = value[0];
- length = strlen(value);
- if ((c == 'n') && (strncmp(value, "none", length) == 0)) {
- *fillPtr = FILL_NONE;
- } else if ((c == 'x') && (strncmp(value, "x", length) == 0)) {
- *fillPtr = FILL_X;
- } else if ((c == 'y') && (strncmp(value, "y", length) == 0)) {
- *fillPtr = FILL_Y;
- } else if ((c == 'b') && (strncmp(value, "both", length) == 0)) {
- *fillPtr = FILL_BOTH;
- } else {
- Tcl_AppendResult(interp, "bad fill argument \"", value,
- "\": should be none, x, y, or both", (char *)NULL);
- return TCL_ERROR;
- }
- return (TCL_OK);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * PrintFill --
- *
- * Returns the fill style string based upon the fill flags.
- *
- * Results:
- * The fill style string is returned.
- *
- *----------------------------------------------------------------------
- */
- /*ARGSUSED*/
- static char *
- PrintFill(clientData, tkwin, widgRec, offset, freeProcPtr)
- ClientData clientData; /* not used */
- Tk_Window tkwin; /* not used */
- char *widgRec; /* Row/column structure record */
- int offset; /* Offset of fill in Partition record */
- Tcl_FreeProc **freeProcPtr; /* not used */
- {
- FillFlags fill = *(FillFlags *)(widgRec + offset);
-
- return (fillStrings[(int)fill]);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ParseJustify --
- *
- * Converts the justification string into its numeric
- * representation. This configuration option affects how the
- * slave window is positioned with respect to the line on which
- * it sits.
- *
- * Valid style strings are:
- *
- * "top" Uppermost point of region is top of the line's
- * text
- * "center" Center point of region is line's baseline.
- * "bottom" Lowermost point of region is bottom of the
- * line's text
- *
- * Returns:
- * A standard Tcl result. If the value was not valid
- *
- *---------------------------------------------------------------------- */
- /*ARGSUSED*/
- static int
- ParseJustify(clientData, interp, tkwin, value, widgRec, offset)
- ClientData clientData; /* not used */
- Tcl_Interp *interp; /* Interpreter to send results back to */
- Tk_Window tkwin; /* not used */
- char *value; /* Justification string */
- char *widgRec; /* Structure record */
- int offset; /* Offset of justify in record */
- {
- Justify *justPtr = (Justify *)(widgRec + offset);
- int length;
- char c;
-
- c = value[0];
- length = strlen(value);
- if ((c == 'c') && (strncmp(value, "center", length) == 0)) {
- *justPtr = JUSTIFY_CENTER;
- } else if ((c == 't') && (strncmp(value, "top", length) == 0)) {
- *justPtr = JUSTIFY_TOP;
- } else if ((c == 'b') && (strncmp(value, "bottom", length) == 0)) {
- *justPtr = JUSTIFY_BOTTOM;
- } else {
- Tcl_AppendResult(interp, "bad justification argument \"", value,
- "\": should be center, top, or bottom", (char *)NULL);
- return TCL_ERROR;
- }
- return (TCL_OK);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * PrintJustify --
- *
- * Returns the justification style string based upon the value.
- *
- * Results:
- * The justification style string is returned.
- *
- *----------------------------------------------------------------------
- */
- /*ARGSUSED*/
- static char *
- PrintJustify(clientData, tkwin, widgRec, offset, freeProcPtr)
- ClientData clientData; /* not used */
- Tk_Window tkwin; /* not used */
- char *widgRec; /* Structure record */
- int offset; /* Offset of justify record */
- Tcl_FreeProc **freeProcPtr; /* not used */
- {
- Justify justify = *(Justify *)(widgRec + offset);
-
- return (justifyStrings[(int)justify]);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * EventuallyRedraw --
- *
- * Queues a request to redraw the text window at the next idle
- * point.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Information gets redisplayed. Right now we don't do selective
- * redisplays: the whole window will be redrawn. This doesn't
- * seem to hurt performance noticeably, but if it does then this
- * could be changed.
- *
- *----------------------------------------------------------------------
- */
- static void
- EventuallyRedraw(textPtr)
- register Htext *textPtr; /* Information about widget. */
- {
- if ((textPtr->tkwin != NULL) && !(textPtr->flags & REDRAW_PENDING)) {
- textPtr->flags |= REDRAW_PENDING;
- Tk_DoWhenIdle(DisplayText, (ClientData)textPtr);
- }
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * DestroyText --
- *
- * This procedure is invoked by Tk_EventuallyFree or Tk_Release
- * to clean up the internal structure of a Htext at a safe time
- * (when no-one is using it anymore).
- *
- * Results:
- * None.
- *
- * Side effects:
- * Everything associated with the widget is freed up.
- *
- * ----------------------------------------------------------------------
- */
-
- static void
- DestroyText(clientData)
- ClientData clientData; /* Info about hypertext widget. */
- {
- register Htext *textPtr = (Htext *)clientData;
-
- if (textPtr->gc != NULL) {
- Tk_FreeGC(textPtr->display, textPtr->gc);
- }
- Tk_FreeOptions(configSpecs, (char *)textPtr, textPtr->display, 0);
- FreeAllLines(textPtr);
- Tcl_DeleteHashTable(&(textPtr->subwindows));
- free((char *)textPtr);
- }
-
- /*
- * --------------------------------------------------------------
- *
- * TextEventProc --
- *
- * This procedure is invoked by the Tk dispatcher for various
- * events on hypertext widgets.
- *
- * Results:
- * None.
- *
- * Side effects:
- * When the window gets deleted, internal structures get
- * cleaned up. When it gets exposed, it is redisplayed.
- *
- * --------------------------------------------------------------
- */
-
- static void
- TextEventProc(clientData, eventPtr)
- ClientData clientData; /* Information about window. */
- XEvent *eventPtr; /* Information about event. */
- {
- Htext *textPtr = (Htext *)clientData;
-
- if (eventPtr->type == ConfigureNotify) {
- if ((textPtr->width != Tk_Width(textPtr->tkwin)) ||
- (textPtr->height != Tk_Height(textPtr->tkwin))) {
- textPtr->flags |= (REQUEST_LAYOUT | VIEW_RESIZED);
- EventuallyRedraw(textPtr);
- }
- } else if (eventPtr->type == Expose) {
-
- /*
- * If the Expose event was synthetic (i.e. we manufactured it
- * ourselves during a redraw operation), toggle the bit flag
- * which controls redraws.
- */
-
- if (eventPtr->xexpose.send_event) {
- textPtr->flags ^= IGNORE_EXPOSURES;
- return;
- }
- if ((eventPtr->xexpose.count == 0) &&
- !(textPtr->flags & IGNORE_EXPOSURES)) {
- EventuallyRedraw(textPtr);
- }
- } else if (eventPtr->type == DestroyNotify) {
- Tcl_DeleteCommand(textPtr->interp, Tk_PathName(textPtr->tkwin));
- textPtr->tkwin = NULL;
- if (textPtr->flags & REDRAW_PENDING) {
- Tk_CancelIdleCall(DisplayText, (ClientData)textPtr);
- }
- #if (TK_MINOR_VERSION > 0)
- Tk_EventuallyFree((ClientData)textPtr, (Tcl_FreeProc *)DestroyText);
- #else
- Tk_EventuallyFree((ClientData)textPtr, (Tk_FreeProc *)DestroyText);
- #endif
- }
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * ConfigureHtext --
- *
- * This procedure is called to process an argv/argc list, plus
- * the Tk option database, in order to configure (or reconfigure)
- * a hypertext widget.
- *
- * The layout of the text must be calculated (by ComputeLayout)
- * whenever particular options change; -font, -filename, -linespacing
- * and -text options. If the user has changes one of these options,
- * it must be detected so that the layout can be recomputed. Since the
- * coordinates of the layout are virtual, there is no need to adjust
- * them if physical window attributes (window size, etc.)
- * change.
- *
- * Results:
- * The return value is a standard Tcl result. If TCL_ERROR is
- * returned, then interp->result contains an error message.
- *
- * Side effects:
- * Configuration information, such as text string, colors, font,
- * etc. get set for textPtr; old resources get freed, if there were any.
- * The hypertext is redisplayed.
- *
- * ----------------------------------------------------------------------
- */
-
-
- static int
- ConfigureHtext(interp, textPtr, argc, argv, flags)
- Tcl_Interp *interp; /* Used for error reporting. */
- Htext *textPtr; /* Information about widget; may or may not
- * already have values for some fields. */
- int argc; /* Number of valid entries in argv. */
- char **argv; /* Arguments. */
- int flags; /* Flags to pass to Tk_ConfigureWidget. */
- {
- XGCValues gcValues;
- unsigned long valueMask;
- GC newGC;
-
- if (Tk_ConfigureWidget(interp, textPtr->tkwin, configSpecs, argc, argv,
- (char *)textPtr, flags) != TCL_OK) {
- return TCL_ERROR;
- }
- Tk_SetBackgroundFromBorder(textPtr->tkwin, textPtr->border);
- if (Blt_OptionChanged(configSpecs, "-font", "-linespacing", (char *)NULL)) {
- textPtr->flags |= REQUEST_LAYOUT;
- }
- gcValues.font = textPtr->fontPtr->fid;
- gcValues.foreground = textPtr->normalFg->pixel;
- valueMask = GCForeground | GCFont;
- newGC = Tk_GetGC(textPtr->tkwin, valueMask, &gcValues);
- if (textPtr->gc != NULL) {
- Tk_FreeGC(textPtr->display, textPtr->gc);
- }
- textPtr->gc = newGC;
-
- /*
- * If the either the -text or -file option changed, read in the
- * new text. The -text option supersedes any -file option.
- */
- if (Blt_OptionChanged(configSpecs, "-filename", "-text", (char *)NULL)) {
- int result;
-
- FreeAllLines(textPtr); /* Delete any previous lines */
- CreateTraces(textPtr); /* Create variable traces */
-
- result = IncludeText(interp, textPtr, textPtr->fileName);
-
- DeleteTraces(textPtr);
- if (result == TCL_ERROR) {
- FreeAllLines(textPtr);
- return TCL_ERROR;
- }
- AdjustLinesAllocated(textPtr);
- textPtr->flags |= REQUEST_LAYOUT; /* Mark for layout update */
- }
- EventuallyRedraw(textPtr);
- return TCL_OK;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * NewLine --
- *
- * This procedure creates and initializes a new line of text.
- *
- * Results:
- * The return value is a pointer to a structure describing the new
- * line of text. If an error occurred, then the return value is NULL
- * and an error message is left in interp->result.
- *
- * Side effects:
- * Memory is allocated.
- *
- * ----------------------------------------------------------------------
- */
- static Line *
- NewLine(textPtr)
- Htext *textPtr;
- {
- Line *linePtr;
-
- if (textPtr->numLines >= textPtr->arraySize) {
- if (textPtr->arraySize == 0) {
- textPtr->arraySize = LINES_ALLOC_CHUNK;
- } else {
- textPtr->arraySize += textPtr->arraySize;
- }
- ResizeArray((char **)&(textPtr->linePtrPtr), sizeof(Line *),
- textPtr->arraySize, textPtr->numLines);
- }
- /* Create new line entry and add to table */
- linePtr = (Line *)calloc(1, sizeof(Line));
- if (linePtr == NULL) {
- textPtr->interp->result = "can't allocate line structure";
- return NULL;
- }
- Blt_InitLinkedList(&(linePtr->windowList), TCL_ONE_WORD_KEYS);
- textPtr->linePtrPtr[textPtr->numLines++] = linePtr;
- return (linePtr);
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * DestroyLine --
- *
- * This procedure is invoked by FreeAllLines to clean up the
- * internal structure of a line.
- *
- * Results: None.
- *
- * Side effects:
- * Everything associated with the line (text and children) is
- * freed up.
- *
- * ----------------------------------------------------------------------
- */
- static void
- DestroyLine(linePtr)
- register Line *linePtr;
- {
- Blt_ListEntry *entryPtr;
- Child *childPtr;
-
- /* Free the list of child structures */
-
- for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
- entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
- childPtr = (Child *)Blt_GetListValue(entryPtr);
- DestroyChild(childPtr);
- }
- Blt_ClearList(&(linePtr->windowList));
- if (linePtr->text != NULL) {
- free(linePtr->text);
- }
- free((char *)linePtr);
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * AppendChild --
- *
- * This procedure creates and initializes a new hyper text child.
- *
- * Results:
- * The return value is a standard Tcl result.
- *
- * Side effects:
- * Memory is allocated. Child gets configured.
- *
- * ----------------------------------------------------------------------
- */
- static int
- AppendChild(textPtr, interp, argc, argv)
- Htext *textPtr; /* Hypertext widget */
- Tcl_Interp *interp; /* Interpreter associated with widget */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- Line *linePtr;
- Child *childPtr;
- Blt_ListEntry *entryPtr;
-
- if (argc < 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " append pathName ?options?\"", (char *)NULL);
- return TCL_ERROR;
- }
- childPtr = CreateChild(textPtr, argv[2]);
- if (childPtr == NULL) {
- return TCL_ERROR;
- }
- if (Tk_ConfigureWidget(interp, textPtr->tkwin, childConfigSpecs,
- argc - 3, argv + 3, (char *)childPtr, 0) != TCL_OK) {
- return TCL_ERROR;
- }
- /*
- * Append child to list of subwindows of the last line.
- */
- linePtr = GetLastLine(textPtr);
- entryPtr = Blt_CreateListEntry((char *)childPtr->tkwin);
- Blt_LinkListAfter(&(linePtr->windowList), entryPtr,
- (Blt_ListEntry *)NULL);
- Blt_SetListValue(entryPtr, childPtr);
- linePtr->width += childPtr->cavityWidth;
- childPtr->precedingTextEnd = linePtr->numChars;
-
- textPtr->flags |= REQUEST_LAYOUT;
- EventuallyRedraw(textPtr);
- return TCL_OK;
- }
-
- /*
- * -----------------------------------------------------------------
- *
- * TranslateAnchor --
- *
- * Translate the coordinates of a given bounding box based
- * upon the anchor specified. The anchor indicates where
- * the given xy position is in relation to the bounding box.
- *
- * nw --- n --- ne
- * | | x,y ---+
- * w center e | |
- * | | +-----+
- * sw --- s --- se
- *
- * Results:
- * The translated coordinates of the bounding box are returned.
- *
- * -----------------------------------------------------------------
- */
- static XPoint
- TranslateAnchor(deltaX, deltaY, anchor)
- int deltaX, deltaY; /* Difference between outer and inner regions
- */
- Tk_Anchor anchor; /* Direction of the anchor */
- {
- XPoint anchorPos;
-
- anchorPos.x = anchorPos.y = 0;
- switch (anchor) {
- case TK_ANCHOR_NW: /* Upper left corner */
- break;
- case TK_ANCHOR_W: /* Left center */
- anchorPos.y = (deltaY / 2);
- break;
- case TK_ANCHOR_SW: /* Lower left corner */
- anchorPos.y = deltaY;
- break;
- case TK_ANCHOR_N: /* Top center */
- anchorPos.x = (deltaX / 2);
- break;
- case TK_ANCHOR_CENTER: /* Centered */
- anchorPos.x = (deltaX / 2);
- anchorPos.y = (deltaY / 2);
- break;
- case TK_ANCHOR_S: /* Bottom center */
- anchorPos.x = (deltaX / 2);
- anchorPos.y = deltaY;
- break;
- case TK_ANCHOR_NE: /* Upper right corner */
- anchorPos.x = deltaX;
- break;
- case TK_ANCHOR_E: /* Right center */
- anchorPos.x = deltaX;
- anchorPos.y = (deltaY / 2);
- break;
- case TK_ANCHOR_SE: /* Lower right corner */
- anchorPos.x = deltaX;
- anchorPos.y = deltaY;
- break;
- }
- return (anchorPos);
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * CreateChild --
- *
- * This procedure creates and initializes a new child subwindow
- * in the hyper text widget.
- *
- * Results:
- * The return value is a pointer to a structure describing the
- * new child. If an error occurred, then the return value is
- * NULL and an error message is left in interp->result.
- *
- * Side effects:
- * Memory is allocated. Child window is mapped. Callbacks are set
- * up for subwindow resizes and geometry requests.
- *
- * ----------------------------------------------------------------------
- */
- static Child *
- CreateChild(textPtr, name)
- Htext *textPtr; /* Hypertext widget */
- char *name; /* Name of child window */
- {
- register Child *childPtr;
- Tk_Window tkwin;
- char buf[BUFSIZ];
- Tcl_HashEntry *entryPtr;
- int dummy;
-
- if (name[0] != '.') { /* Relative path, make absolute */
- sprintf(buf, "%s.%s", Tk_PathName(textPtr->tkwin), name);
- name = buf;
- }
- /* Get the Tk window and parent Tk window associated with the child */
- tkwin = Tk_NameToWindow(textPtr->interp, name, textPtr->tkwin);
- if (tkwin == NULL) {
- return NULL;
- }
- if (FindChild(textPtr, name) != NULL) {
- Tcl_AppendResult(textPtr->interp, "\"", name,
- "\" is already appended to ", Tk_PathName(textPtr->tkwin),
- (char *)NULL);
- return NULL;
- }
- if (textPtr->tkwin != Tk_Parent(tkwin)) {
- Tcl_AppendResult(textPtr->interp, "\"", name,
- "\" is not a child of ", Tk_PathName(textPtr->tkwin),
- (char *)NULL);
- return NULL;
- }
- childPtr = (Child *)calloc(1, sizeof(Child));
- if (childPtr == NULL) {
- textPtr->interp->result = "can't create child structure";
- return NULL;
- }
- childPtr->tkwin = tkwin;
- childPtr->textPtr = textPtr;
- childPtr->x = childPtr->y = 0;
- childPtr->fill = FILL_NONE;
- childPtr->justify = JUSTIFY_CENTER;
- childPtr->anchor = TK_ANCHOR_CENTER;
- entryPtr = Tcl_CreateHashEntry(&(textPtr->subwindows), (char *)tkwin,
- &dummy);
- Tcl_SetHashValue(entryPtr, (ClientData)childPtr);
-
- Tk_ManageGeometry(tkwin, &ChildGeometry, (ClientData)childPtr);
- Tk_CreateEventHandler(tkwin, StructureNotifyMask, ChildStructureProc,
- (ClientData)childPtr);
- return (childPtr);
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * DestroyChild --
- *
- * This procedure is invoked by DestroyLine to clean up the
- * internal structure of a child.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Everything associated with the widget is freed up.
- *
- * ----------------------------------------------------------------------
- */
- static void
- DestroyChild(childPtr)
- register Child *childPtr;
- {
- /* Destroy the child window if it still exists */
- if (childPtr->tkwin != NULL) {
- Tcl_HashEntry *entryPtr;
-
- Tk_DeleteEventHandler(childPtr->tkwin, StructureNotifyMask,
- ChildStructureProc, (ClientData)childPtr);
- entryPtr = Tcl_FindHashEntry(&(childPtr->textPtr->subwindows),
- (char *)childPtr->tkwin);
- Tcl_DeleteHashEntry(entryPtr);
- Tk_DestroyWindow(childPtr->tkwin);
- }
- free((char *)childPtr);
- }
-
- /*
- * --------------------------------------------------------------
- *
- * TextEventProc --
- *
- * This procedure is invoked by the Tk dispatcher for various
- * events on hypertext widgets.
- *
- * Results:
- * None.
- *
- * Side effects:
- * When the window gets deleted, internal structures get
- * cleaned up. When it gets exposed, it is redisplayed.
- *
- * --------------------------------------------------------------
- */
- static void
- ChildStructureProc(clientData, eventPtr)
- ClientData clientData; /* Information about window. */
- XEvent *eventPtr; /* Information about event. */
- {
- register Child *childPtr = (Child *)clientData;
- Htext *textPtr;
-
- if ((childPtr == NULL) || (childPtr->tkwin == NULL)) {
- return;
- }
- textPtr = childPtr->textPtr;
-
- if (eventPtr->type == DestroyNotify) {
- /*
- * Mark the child as deleted by dereferencing the Tk window
- * pointer. Zero out the height and width to collapse the area
- * used by the child. Redraw the window only if the child is
- * currently visible and mapped.
- */
- childPtr->textPtr->flags |= REQUEST_LAYOUT;
- if (Tk_IsMapped(childPtr->tkwin) && (childPtr->flags & VISIBLE)) {
- EventuallyRedraw(textPtr);
- }
- Tk_DeleteEventHandler(childPtr->tkwin, StructureNotifyMask,
- ChildStructureProc, (ClientData)childPtr);
- Tcl_DeleteHashEntry(Tcl_FindHashEntry(&(childPtr->textPtr->subwindows),
- (char *)childPtr->tkwin));
- childPtr->tkwin = NULL;
- } else if (eventPtr->type == ConfigureNotify) {
-
- /*
- * Children can't request new positions by themselves, so worry
- * only about resizing.
- */
-
- if (childPtr->windowWidth != Tk_Width(childPtr->tkwin) ||
- childPtr->windowHeight != Tk_Height(childPtr->tkwin)) {
- EventuallyRedraw(textPtr);
- textPtr->flags |= REQUEST_LAYOUT;
- }
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ComputeLayout --
- *
- * This procedure computes the total width and height needed
- * to contain the text and children from all the lines of text.
- * It merely sums the heights and finds the maximum width of
- * all the lines. The width and height are needed for scrolling.
- *
- * Results:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static void
- ComputeLayout(textPtr)
- Htext *textPtr;
- {
- register int count;
- register Line **linePtrPtr;
- register int height, width;
-
- width = height = 0;
- linePtrPtr = textPtr->linePtrPtr;
- for (count = 0; count < textPtr->numLines; count++) {
- (*linePtrPtr)->offset = height;
- LayoutLine(textPtr, *linePtrPtr);
- height += (*linePtrPtr)->height;
- if ((*linePtrPtr)->width > width) {
- width = (*linePtrPtr)->width;
- }
- linePtrPtr++;
- }
- /*
- * Set changed flag if new layout changed size of virtual text.
- */
- if ((height != textPtr->vPort.height) ||
- (width != textPtr->vPort.width)) {
- textPtr->flags |= LAYOUT_CHANGED;
- textPtr->vPort.height = height;
- textPtr->vPort.width = width;
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetReqWidth --
- *
- * Returns the width requested by the child window. The requested
- * space also includes any internal padding which has been designated
- * for this window.
- *
- * Results:
- * Returns the requested width of the child window.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- GetReqWidth(childPtr)
- Child *childPtr;
- {
- int width;
-
- if (childPtr->reqWidth > 0) {
- width = childPtr->reqWidth;
- } else if (childPtr->relWidth > 0.0) {
- width = (int)((double)Tk_Width(childPtr->textPtr->tkwin) *
- childPtr->relWidth + 0.5);
- } else {
- width = Tk_ReqWidth(childPtr->tkwin) + 2 * (childPtr->ipadX);
- }
- return (width);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetReqHeight --
- *
- * Returns the height requested by the child window. The requested
- * space also includes any internal padding which has been designated
- * for this window.
- *
- * Results:
- * Returns the requested height of the child window.
- *
- *----------------------------------------------------------------------
- */
- static int
- GetReqHeight(childPtr)
- Child *childPtr;
- {
- int height;
-
- if (childPtr->reqHeight > 0) {
- height = childPtr->reqHeight;
- } else if (childPtr->relHeight > 0.0) {
- height = (int)((double)Tk_Height(childPtr->textPtr->tkwin) *
- childPtr->relHeight + 0.5);
- } else {
- height = Tk_ReqHeight(childPtr->tkwin) + 2 * (childPtr->ipadY);
- }
- return (height);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetCavityWidth --
- *
- * Returns the width of the cavity based upon the requested size
- * of the child window. The requested space also includes any
- * external padding which has been designated for this window.
- *
- * Results:
- * Returns the requested width of the cavity.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- GetCavityWidth(childPtr)
- Child *childPtr;
- {
- int width;
-
- width = GetReqWidth(childPtr) +
- (2 * (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padX));
- return (width);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetCavityHeight --
- *
- * Returns the height of the cavity based upon the requested size
- * of the child window. The requested space also includes any
- * external padding which has been designated for this window.
- *
- * Results:
- * Returns the requested height of the cavity.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- GetCavityHeight(childPtr)
- Child *childPtr;
- {
- int height;
-
- height = GetReqHeight(childPtr) +
- (2 * (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padY));
- return (height);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * LayoutLine --
- *
- * This procedure computes the total width and height needed
- * to contain the text and children for a particular line.
- * It also calculates the baseline of the text on the line with
- * respect to the other children on the line.
- *
- * Results:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static void
- LayoutLine(textPtr, linePtr)
- Htext *textPtr;
- Line *linePtr;
- {
- register Child *childPtr;
- int numChars;
- int maxAscent, maxDescent, maxHeight;
- int ascent, descent;
- register int curPos = 0;
- int median; /* Difference of font ascent/descent values */
- Blt_ListEntry *entryPtr;
- register int x, y;
- int newX;
-
- /*
- * Pass 1: Determine the maximum ascent (baseline) and descent
- * needed for the line. We'll need this for figuring the top,
- * bottom, and center anchors.
- */
- /* Initialize line defaults */
- maxAscent = textPtr->fontPtr->ascent;
- maxDescent = textPtr->fontPtr->descent;
- median = textPtr->fontPtr->ascent - textPtr->fontPtr->descent;
- ascent = descent = 0; /* Suppress compiler warnings */
- for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
- entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
- childPtr = (Child *)Blt_GetListValue(entryPtr);
- if (childPtr->tkwin == NULL) {
- continue;
- }
- childPtr->cavityHeight = GetCavityHeight(childPtr);
- childPtr->cavityWidth = GetCavityWidth(childPtr);
- switch (childPtr->justify) {
- case JUSTIFY_TOP:
- ascent = textPtr->fontPtr->ascent + childPtr->padY;
- descent = childPtr->cavityHeight - textPtr->fontPtr->ascent;
- break;
- case JUSTIFY_CENTER:
- ascent = (childPtr->cavityHeight + median) / 2;
- descent = (childPtr->cavityHeight - median) / 2;
- break;
- case JUSTIFY_BOTTOM:
- ascent = childPtr->cavityHeight - textPtr->fontPtr->descent;
- descent = textPtr->fontPtr->descent;
- break;
- }
- if (descent > maxDescent) {
- maxDescent = descent;
- }
- if (ascent > maxAscent) {
- maxAscent = ascent;
- }
- }
-
- maxHeight = maxAscent + maxDescent + textPtr->lineSpacing;
- x = 0; /* Always starts from x=0 */
- y = 0; /* Suppress compiler warning */
- curPos = 0;
- /*
- * Pass 2: Find the placements of the text and children along each
- * line.
- */
- for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
- entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
- childPtr = (Child *)Blt_GetListValue(entryPtr);
- if (childPtr->tkwin == NULL) {
- continue;
- }
- /* Get the width of the text leading to the child */
- numChars = (childPtr->precedingTextEnd - curPos);
- if ((numChars > 0) && (linePtr->text != NULL)) {
- TkMeasureChars(textPtr->fontPtr, linePtr->text + curPos,
- numChars, x, 10000, 0, TK_PARTIAL_OK | TK_AT_LEAST_ONE, &newX);
- childPtr->precedingTextWidth = newX - x;
- x = newX;
- }
- switch (childPtr->justify) {
- case JUSTIFY_TOP:
- y = maxAscent + textPtr->fontPtr->descent -
- childPtr->cavityHeight;
- break;
- case JUSTIFY_CENTER:
- y = maxAscent - (childPtr->cavityHeight + median) / 2;
- break;
- case JUSTIFY_BOTTOM:
- y = maxAscent + textPtr->fontPtr->descent -
- childPtr->cavityHeight;
- break;
- }
- childPtr->x = x;
- childPtr->y = y;
- curPos = childPtr->precedingTextEnd;
- x += childPtr->cavityWidth;
- }
-
- /*
- * This may be piece of line after last child and will also pick
- * up the entire line if no children occurred on it
- */
- numChars = (linePtr->numChars - curPos);
- if ((numChars > 0) && (linePtr->text != NULL)) {
- TkMeasureChars(textPtr->fontPtr, linePtr->text + curPos, numChars,
- x, 10000, 0, TK_PARTIAL_OK | TK_AT_LEAST_ONE, &newX);
- x = newX;
- }
- /* Update line parameters */
- linePtr->width = (unsigned int)x;
- linePtr->height = maxHeight;
- linePtr->baseline = maxAscent;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * MoveChild --
- *
- * Move a child subwindow to a new location in the hypertext
- * parent window. If the window has no geometry (i.e. width,
- * or height is 0), simply unmap to window.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Each subwindow is moved to its new location, generating
- * Expose events in the parent for each child window moved.
- *
- * ----------------------------------------------------------------------
- */
- static void
- MoveChild(childPtr, offset)
- register Child *childPtr;
- int offset;
- {
- int winWidth, winHeight;
- int width, height;
- int deltaX, deltaY;
- int x, y;
- int padX, padY;
-
- if (childPtr->tkwin == NULL) {
- return;
- }
- winWidth = GetReqWidth(childPtr);
- winHeight = GetReqHeight(childPtr);
- if ((winWidth < 1) || (winHeight < 1)) {
- if (Tk_IsMapped(childPtr->tkwin)) {
- Tk_UnmapWindow(childPtr->tkwin);
- }
- return;
- }
- padX = (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padX);
- padY = (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padY);
- x = padX + childPtr->x - childPtr->textPtr->vPort.x;
- y = padY + offset + childPtr->y - childPtr->textPtr->vPort.y;
- width = childPtr->cavityWidth - (2 * padX);
- height = childPtr->cavityHeight - (2 * padY);
-
- if ((width < winWidth) || (childPtr->fill & FILL_X)) {
- winWidth = width;
- }
- if ((height < winHeight) || (childPtr->fill & FILL_Y)) {
- winHeight = height;
- }
- deltaX = deltaY = 0;
- if (width > winWidth) {
- deltaX = width - winWidth;
- }
- if (height > winHeight) {
- deltaY = height - winHeight;
- }
- if ((deltaX > 0) || (deltaY > 0)) {
- XPoint anchorPos;
-
- anchorPos = TranslateAnchor(deltaX, deltaY, childPtr->anchor);
- x += anchorPos.x, y += anchorPos.y;
- }
- childPtr->windowWidth = winWidth;
- childPtr->windowHeight = winHeight;
- if ((x != Tk_X(childPtr->tkwin)) || (y != Tk_Y(childPtr->tkwin)) ||
- (winWidth != Tk_Width(childPtr->tkwin)) ||
- (winHeight != Tk_Height(childPtr->tkwin))) {
- Tk_MoveResizeWindow(childPtr->tkwin, x, y, (unsigned int)winWidth,
- (unsigned int)winHeight);
- if (!Tk_IsMapped(childPtr->tkwin)) {
- Tk_MapWindow(childPtr->tkwin);
- }
- }
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * DrawPage --
- *
- * This procedure displays the lines of text and moves the child
- * windows to their new positions. It draws lines with regard to
- * the direction of the scrolling. The idea here is to make the
- * text and buttons appear to move together. Otherwise you will
- * get a "jiggling" effect where the windows appear to bump into
- * the next line before that line is moved. In the worst case, where
- * every line has at least one widget, you can get an aquarium effect
- * (lines appear to ripple up).
- *
- * The text area may start between line boundaries (to accommodate
- * both variable height lines and constant scrolling). Subtract the
- * difference of the page offset and the line offset from the starting
- * coordinates. For horizontal scrolling, simply subtract the offset
- * of the viewport. The window will clip the top of the first line,
- * the bottom of the last line, whatever text extends to the left
- * or right of the viewport on any line.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Commands are output to X to display the line in its current
- * mode.
- *
- * ----------------------------------------------------------------------
- */
- static void
- DrawPage(textPtr, deltaY)
- Htext *textPtr;
- int deltaY; /* Change from previous Y coordinate */
- {
- Line *linePtr;
- Child *childPtr;
- Tk_Window tkwin = textPtr->tkwin;
- int numChars;
- int curPos;
- int baseline;
- Pixmap pixMap;
- int forceCopy = 0;
- register int i;
- int curLine, lastY;
- register int x, y;
- Blt_ListEntry *entryPtr;
-
- /* Setup: Clear the display */
- /* Create an off-screen pixmap for semi-smooth scrolling. */
- pixMap = XCreatePixmap(textPtr->display, Tk_WindowId(tkwin),
- Tk_Width(tkwin), Tk_Height(tkwin),
- DefaultDepthOfScreen(Tk_Screen(tkwin)));
- Tk_Fill3DRectangle(tkwin, pixMap, textPtr->border, 0, 0,
- Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
- x = -(textPtr->vPort.x);
- y = -(textPtr->vPort.y);
-
- if (deltaY >= 0) {
- y += textPtr->linePtrPtr[textPtr->first]->offset;
- curLine = textPtr->first;
- lastY = 0;
- } else {
- y += textPtr->linePtrPtr[textPtr->last]->offset;
- curLine = textPtr->last;
- lastY = Tk_Height(tkwin);
- }
- forceCopy = 0;
-
- /* Draw each line */
- for (i = textPtr->first; i <= textPtr->last; i++) {
-
- /* Initialize character position in text buffer to start */
- curPos = 0;
- /* Initialize X position */
- x = -(textPtr->vPort.x);
-
- linePtr = textPtr->linePtrPtr[curLine];
- baseline = y + linePtr->baseline; /* Base line in window
- * coordinates */
-
- for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
- entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
- childPtr = (Child *)Blt_GetListValue(entryPtr);
- MoveChild(childPtr, linePtr->offset);
- childPtr->flags |= VISIBLE;
- numChars = (childPtr->precedingTextEnd - curPos);
- if ((numChars > 0) && (linePtr->text != NULL)) {
- TkDisplayChars(textPtr->display, pixMap, textPtr->gc,
- textPtr->fontPtr, linePtr->text + curPos,
- numChars, x, baseline, 0, 0);
- x += childPtr->precedingTextWidth;
- }
- curPos = childPtr->precedingTextEnd;
- x += childPtr->cavityWidth;
- forceCopy++;
- }
-
- /*
- * This may be the text trailing the last child or the entire
- * line if no children occur on it.
- */
- numChars = (linePtr->numChars - curPos);
- if ((numChars > 0) && (linePtr->text != NULL)) {
- #ifdef notdef
- if (textPtr->selectFirst >= curLine &&
- textPtr->selectLast <= curLine) {
- Tk_Fill3DRectangle(textPtr->display, pixMap,
- textPtr->selectBorder, y, 0,
- numChars, Tk_Height(tkwin),
- 0, textPtr->selectBorderWidth);
- }
- #endif
- TkDisplayChars(textPtr->display, pixMap, textPtr->gc,
- textPtr->fontPtr, linePtr->text + curPos,
- numChars, x, baseline, 0, 0);
- }
- /* Go to the top of the next line */
- if (deltaY >= 0) {
- y += textPtr->linePtrPtr[curLine++]->height;
- }
- if (forceCopy > 0 && !(textPtr->flags & VIEW_RESIZED)) {
- if (deltaY >= 0) {
- XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
- textPtr->gc, 0, lastY, Tk_Width(tkwin), y - lastY,
- 0, lastY);
- } else {
- XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
- textPtr->gc, 0, y, Tk_Width(tkwin), lastY - y,
- 0, y);
- }
- forceCopy = 0; /* Reset drawing flag */
- lastY = y; /* Record last Y position */
- }
- if ((deltaY < 0) && (curLine > 0)) {
- y -= textPtr->linePtrPtr[--curLine]->height;
- }
- }
- /*
- * If the viewport was resized, draw the page in one operation.
- * Otherwise draw any left-over block of text (either at the top
- * or bottom of the page)
- */
- if (textPtr->flags & VIEW_RESIZED) {
- XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
- textPtr->gc, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
- } else if (lastY != y) {
- if (deltaY >= 0) {
- XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
- textPtr->gc, 0, lastY, Tk_Width(tkwin),
- Tk_Height(tkwin) - lastY, 0, lastY);
- } else {
- XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
- textPtr->gc, 0, 0, Tk_Width(tkwin), lastY, 0, 0);
- }
- }
- XFreePixmap(textPtr->display, pixMap);
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * DisplayText --
- *
- * This procedure is invoked to display a hypertext widget.
- * Many of the operations which might ordinarily be performed
- * elsewhere (e.g. in a configuration routine) are done here
- * because of the somewhat unusual interactions occurring between
- * the parent and child windows.
- *
- * Recompute the layout of the text if necessary. This is
- * necessary if the world coordinate system has changed.
- * Specifically, the following may have occurred:
- *
- * - a text attribute has changed (font, linespacing, etc.).
- * - child option changed (anchor, width, height).
- * - actual child window was resized.
- * - new text string or file.
- *
- * This is deferred to the display routine since potentially
- * many of these may occur (especially child window changes).
- *
- * Set the vertical and horizontal scrollbars (if they are
- * designated) by issuing a Tcl command. Done here since
- * the text window width and height are needed.
- *
- * If the viewport position or contents have changed in the
- * vertical direction, the now out-of-view child windows
- * must be moved off the viewport. Since child windows will
- * obscure the text window, it is imperative that the children
- * are moved off before we try to redraw text in the same area.
- * This is necessary only for vertical movements. Horizontal
- * child window movements are handled automatically in the
- * page drawing routine.
- *
- * Get the new first and last line numbers for the viewport.
- * These line numbers may have changed because either a)
- * the viewport changed size or position, or b) the text
- * (child window sizes or text attributes) have changed.
- *
- * If the viewport has changed vertically (i.e. the first or
- * last line numbers have changed), move the now out-of-view
- * child windows off the viewport.
- *
- * Potentially many expose events may be generated when the
- * the individual child windows are moved and/or resized.
- * These events need to be ignored. Since (I think) expose
- * events are guaranteed to happen in order, we can bracket
- * them by sending phony events (via XSendEvent). The phony
- * event turn on and off flags which indicate if the events
- * should be ignored.
- *
- * Finally, the page drawing routine is called.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Commands are output to X to display the hypertext in its
- * current mode.
- *
- * ----------------------------------------------------------------------
- */
- static void
- DisplayText(clientData)
- ClientData clientData; /* Information about widget. */
- {
- Htext *textPtr = (Htext *)clientData;
- register Tk_Window tkwin;
- int oldFirst; /* First line of old viewport */
- int oldLast; /* Last line of old viewport */
- int deltaY; /* Change in viewport in Y direction */
-
- textPtr->flags &= ~REDRAW_PENDING;
-
- tkwin = textPtr->tkwin;
- if ((tkwin == NULL) || (textPtr->numLines == 0)) {
- return;
- }
- if (textPtr->flags & REQUEST_LAYOUT) {
- /*
- * Recompute the layout when children are created, deleted,
- * moved, or resized. Also when text attributes (such as
- * font, linespacing) have changed.
- */
- ComputeLayout(textPtr);
- }
- textPtr->width = (unsigned int)textPtr->reqWidth;
- textPtr->height = (unsigned int)textPtr->reqHeight;
- if (textPtr->width == 0) {
- textPtr->width = BLT_MIN(textPtr->vPort.width, textPtr->maxWidth);
- }
- if (textPtr->height == 0) {
- textPtr->height = BLT_MIN(textPtr->vPort.height, textPtr->maxHeight);
- }
- if ((textPtr->width != Tk_ReqWidth(tkwin)) ||
- (textPtr->height != Tk_ReqHeight(tkwin))) {
- Tk_GeometryRequest(tkwin, textPtr->width, textPtr->height);
- textPtr->flags |= REQUEST_LAYOUT;
- EventuallyRedraw(textPtr);
- return;
- }
- if (!Tk_IsMapped(tkwin)) {
- return;
- }
- /*
- * Turn off layout requests here, after the text window has been
- * mapped Otherwise, relative slave window height and width
- * requests wrt to the text window will be wrong.
- */
- textPtr->flags &= ~REQUEST_LAYOUT;
-
- /* Is there a pending gotoline request? */
- if (textPtr->flags & REQUEST_GOTO) {
- textPtr->pendingY = textPtr->linePtrPtr[textPtr->reqLineNum]->offset;
- textPtr->flags &= ~REQUEST_GOTO;
- }
- deltaY = textPtr->pendingY - textPtr->vPort.y;
- oldFirst = textPtr->first, oldLast = textPtr->last;
-
- /*
- * If the viewport has changed size or position, or the text
- * and/or child subwindows have changed, adjust the scrollbars to
- * new positions.
- */
- if (textPtr->flags & (VIEW_MOVED | VIEW_RESIZED | LAYOUT_CHANGED)) {
- /* Reset viewport origin and world extents */
- textPtr->vPort.x = textPtr->pendingX;
- textPtr->vPort.y = textPtr->pendingY;
- if (textPtr->xScrollCmd != NULL)
- UpdateScrollbar(textPtr->interp, textPtr->xScrollCmd,
- textPtr->vPort.width, Tk_Width(tkwin), textPtr->vPort.x,
- textPtr->scrollX);
- if (textPtr->yScrollCmd != NULL)
- UpdateScrollbar(textPtr->interp, textPtr->yScrollCmd,
- textPtr->vPort.height, Tk_Height(tkwin),
- textPtr->vPort.y, textPtr->scrollY);
- /*
- * Given a new viewport or text height, find the first and
- * last line numbers of the new viewport.
- */
- GetVisibleLines(textPtr);
- }
- /*
- * (This is a kludge.) Send an expose event before and after
- * drawing the page of text. Since moving and resizing of the
- * subwindows will cause redundant expose events in the parent
- * window, the phony events will bracket them indicating no action
- * should be taken.
- */
- SendBogusEvent(tkwin);
-
- /*
- * If either the position of the viewport has changed or the size
- * of width or height of the entire text have changed, move the
- * children from the previous viewport out of the current
- * viewport. Worry only about the vertical child window movements.
- * The horizontal moves are handled by the when drawing the page
- * of text.
- */
- if (textPtr->first != oldFirst || textPtr->last != oldLast) {
- register Line **linePtrPtr;
- register int i;
- int first, last;
- Blt_ListEntry *entryPtr;
- Child *childPtr;
-
- /* Figure out which lines are now out of the viewport */
-
- if ((textPtr->first > oldFirst) && (textPtr->first <= oldLast)) {
- first = oldFirst, last = textPtr->first;
- } else if ((textPtr->last < oldLast) && (textPtr->last >= oldFirst)) {
- first = textPtr->last, last = oldLast;
- } else {
- first = oldFirst, last = oldLast;
- }
-
- linePtrPtr = &(textPtr->linePtrPtr[first]);
- for (i = first; i <= last; i++) {
- for (entryPtr = Blt_FirstListEntry(&((*linePtrPtr)->windowList));
- entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
- childPtr = (Child *)Blt_GetListValue(entryPtr);
- MoveChild(childPtr, (*linePtrPtr)->offset);
- childPtr->flags &= ~VISIBLE;
- }
- linePtrPtr++;
- }
- }
- DrawPage(textPtr, deltaY);
- SendBogusEvent(tkwin);
-
- /* Reset flags */
- textPtr->flags &= ~(VIEW_RESIZED | VIEW_MOVED | LAYOUT_CHANGED);
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * GetVisibleLines --
- *
- * Calculates which lines are visible using the height
- * of the viewport and y offset from the top of the text.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Only those line between first and last inclusive are
- * redrawn.
- *
- * ----------------------------------------------------------------------
- */
- static int
- GetVisibleLines(textPtr)
- Htext *textPtr;
- {
- int topLine, bottomLine;
- int topPos, bottomPos;
- int lastLine;
-
- topPos = textPtr->pendingY;
- lastLine = textPtr->numLines - 1;
- /* First line */
- topLine = LineSearch(textPtr, topPos, 0, lastLine);
- if (topLine < 0) {
- /*
- * This can't be. The newY offset must be corrupted.
- */
- fprintf(stderr, "First position not found `%d'", topPos);
- return TCL_ERROR;
- }
- textPtr->first = topLine;
-
- /*
- * If there is less text than window space, the bottom line is the
- * last line of text. Otherwise search for the line at the bottom
- * of the window.
- */
- bottomPos = topPos + Tk_Height(textPtr->tkwin) - 1;
- if (bottomPos < textPtr->vPort.height) {
- bottomLine = LineSearch(textPtr, bottomPos, topLine, lastLine);
- } else {
- bottomLine = lastLine;
- }
- if (bottomLine < 0) {
- /*
- * This can't be. The newY offset must be corrupted.
- */
- fprintf(stderr, "Last position not found `%d'\n", bottomPos);
- #ifdef notdef
- fprintf(stderr, "vPort.height=%d,height=%d,top=%d,first=%d,last=%d\n",
- textPtr->vPort.height, Tk_Height(textPtr->tkwin), topPos,
- textPtr->linePtrPtr[topLine]->offset,
- textPtr->linePtrPtr[lastLine]->offset);
- #endif
- return TCL_ERROR;
- }
- textPtr->last = bottomLine;
- return TCL_OK;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * LineSearch --
- *
- * Performs a binary search for the line located at some world
- * Y coordinate. The search is limited to those lines between
- * low and high inclusive.
- *
- * Results:
- * Returns the line number at the given Y coordinate. If position
- * does not correspond to any of the lines in the given the set,
- * -1 is returned.
- *
- * ----------------------------------------------------------------------
- */
- static int
- LineSearch(textPtr, position, low, high)
- Htext *textPtr;
- int position;
- int low, high;
- {
- register int midPoint;
- register Line *linePtr;
-
- while (low <= high) {
- midPoint = (low + high) >> 1;
- linePtr = textPtr->linePtrPtr[midPoint];
- if (position < linePtr->offset) {
- high = midPoint - 1;
- } else if (position >= (linePtr->offset + linePtr->height)) {
- low = midPoint + 1;
- } else {
- return (midPoint);
- }
- }
- return -1;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * UpdateScrollbar --
- *
- * Invoke a Tcl command to the scrollbar, defining the new
- * position and length of the scroll. See the Tk documentation
- * for further information on the scrollbar. It is assumed the
- * scrollbar command prefix is valid.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Scrollbar is commanded to change position and/or size.
- *
- * ----------------------------------------------------------------------
- */
- static void
- UpdateScrollbar(interp, command, total, window, first, units)
- Tcl_Interp *interp;
- char *command; /* scrollbar command */
- int total; /* Total distance */
- int window; /* Window distance */
- int first; /* Position of viewport */
- int units; /* Unit distance */
- {
- char scrollArgs[1000];
- int totalUnits, windowUnits;
- int firstUnit, lastUnit;
-
- totalUnits = (total / units) + 1;
- windowUnits = window / units;
- firstUnit = first / units;
- lastUnit = (firstUnit + windowUnits);
-
- /* Keep scrolling within range. */
- if (firstUnit >= totalUnits) {
- firstUnit = totalUnits;
- }
- if (lastUnit > totalUnits) {
- lastUnit = totalUnits;
- }
- sprintf(scrollArgs, " %d %d %d %d", totalUnits, windowUnits,
- firstUnit, lastUnit);
- if (Tcl_VarEval(interp, command, scrollArgs, (char *)NULL) != TCL_OK) {
- Tk_BackgroundError(interp);
- }
- }
-
-
- static void
- FreeAllLines(textPtr)
- Htext *textPtr;
- {
- register int i;
-
- for (i = 0; i < textPtr->numLines; i++) {
- DestroyLine(textPtr->linePtrPtr[i]);
- }
- if (textPtr->linePtrPtr != NULL) {
- free((char *)textPtr->linePtrPtr);
- }
- textPtr->linePtrPtr = NULL;
- textPtr->arraySize = textPtr->numLines = 0;
- }
-
-
- static void
- AdjustLinesAllocated(textPtr)
- Htext *textPtr;
- {
- if (textPtr->arraySize > 0) {
- ResizeArray((char **)&(textPtr->linePtrPtr), sizeof(Line *),
- textPtr->numLines, textPtr->arraySize);
- textPtr->arraySize = textPtr->numLines;
- textPtr->first = 0;
- textPtr->last = textPtr->numLines - 1;
- textPtr->pendingX = textPtr->pendingY = 0;
- textPtr->vPort.width = textPtr->vPort.height = 0;
- textPtr->vPort.x = textPtr->vPort.y = 0;
- }
- }
-
-
- static Line *
- GetLastLine(textPtr)
- Htext *textPtr;
- {
- Line *linePtr;
-
- if (textPtr->numLines == 0) {
- linePtr = NewLine(textPtr);
-
- if (linePtr == NULL) {
- textPtr->interp->result = "can't allocate line structure";
- return NULL;
- }
- }
- linePtr = textPtr->linePtrPtr[textPtr->numLines - 1];
- return linePtr;
- }
-
- static void
- SetTextInfo(linePtr, text, size)
- Line *linePtr;
- char *text;
- int size;
- {
- linePtr->numChars = size;
- if (linePtr->text != NULL) {
- free((char *)linePtr->text);
- }
- linePtr->text = strdup(text);
- }
-
- static void
- GetTextInfo(linePtr, buffer, sizePtr)
- Line *linePtr;
- char *buffer;
- int *sizePtr;
- {
- *sizePtr = linePtr->numChars;
- if (linePtr->numChars > 0) {
- strcpy(buffer, linePtr->text);
- }
- buffer[linePtr->numChars] = 0;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * ParseInput --
- *
- * Parse the input to the Htext structure into an array of lines.
- *
- * Results:
- * Returns TCL_OK or error depending if the file was read correctly.
- *
- * ----------------------------------------------------------------------
- */
- static int
- ParseInput(interp, textPtr, inputPtr)
- Tcl_Interp *interp;
- Htext *textPtr;
- char *inputPtr;
- {
- register Line *linePtr;
- int c;
-
- #define HUGE_LINE_SIZE 1024
- char buffer[HUGE_LINE_SIZE];
- #define HUGE_COMMAND_SIZE 10000
- char command[HUGE_COMMAND_SIZE];
- int count;
- register int state;
-
- linePtr = GetLastLine(textPtr);
- if (linePtr == NULL) {
- return TCL_ERROR; /* Error allocating line */
- }
- GetTextInfo(linePtr, buffer, &count);
- state = 0;
- while ((c = *inputPtr++) != '\0') {
- if (c == textPtr->specChar) {
- state++;
- } else if (c == '\n') {
- state = -1;
- } else if ((state == 0) && (c == '\\')) {
- state = 3;
- } else {
- state = 0;
- }
-
- switch (state) {
- case 2: /* Tcl Command block found */
- count--;
- inputPtr = CollectCommand(textPtr, inputPtr, command);
- if (inputPtr == NULL) {
- return TCL_ERROR;
- }
- linePtr->numChars = count;
- if (Tcl_Eval(interp, command) != TCL_OK) {
- return TCL_ERROR;
- }
- state = 0;
- break;
-
- case 4: /* Escaped block designator */
- buffer[count - 1] = c;
- state = 0;
- break;
-
- case -1: /* End of text line */
- buffer[count] = '\0';
- SetTextInfo(linePtr, buffer, count);
- linePtr = NewLine(textPtr);
- if (linePtr == NULL) {
- return TCL_ERROR;
- }
- count = state = 0;
- break;
-
- default: /* Default action, add to text buffer */
- buffer[count++] = c;
- break;
- }
- if (count == HUGE_LINE_SIZE) {
- interp->result = "text line is too long";
- return TCL_ERROR;
- }
- }
- if (count > 0) {
- buffer[count] = '\0';
- SetTextInfo(linePtr, buffer, count);
- }
- return TCL_OK;
- }
-
-
- static char *
- ReadFile(interp, fileName)
- Tcl_Interp *interp;
- char *fileName;
- {
- FILE *fp;
- int arraySize;
- register int numBytes;
- char *buffer;
- register int count;
- char *result = NULL;
-
- fp = fopen(fileName, "r");
- if (fp == NULL) {
- Tcl_AppendResult(interp, "can't open \"", fileName,
- "\" for reading: ", Tcl_PosixError(interp), (char *)NULL);
- return NULL;
- }
- count = 0;
- arraySize = BUFSIZ; /* Initial array size */
- if (ResizeArray(&buffer, sizeof(char), arraySize, count) != TCL_OK) {
- interp->result = "can't allocate character array";
- return NULL;
- }
- for (;;) {
- /* Read in next block of text */
- numBytes = fread(&buffer[count], sizeof(char), BUFSIZ, fp);
-
- if (numBytes < 0) {
- Tcl_AppendResult(interp, "error reading \"", fileName, "\": ",
- Tcl_PosixError(interp), (char *)NULL);
- goto error;
- } else if (numBytes == 0) {
- break;
- }
- count += numBytes;
- if (count >= arraySize) {
- /* Reallocate with double the buffer size */
- arraySize += arraySize;
- if (ResizeArray(&buffer, sizeof(char),
- arraySize, count) != TCL_OK) {
- interp->result = "can't allocate character array";
- goto error;
- }
- }
- }
- buffer[count] = '\0';
- result = buffer;
- error:
- fclose(fp);
- return result;
- }
-
-
- static char *
- CollectCommand(textPtr, inputPtr, buffer)
- Htext *textPtr;
- char *inputPtr;
- char *buffer;
- {
- register int c;
- register int state;
- register int count;
-
-
- /* Simply collect the all the characters until %% into a buffer */
-
- state = count = 0;
- while ((c = *inputPtr++) != '\0') {
- if (c == textPtr->specChar) {
- state++;
- } else if ((state == 0) && (c == '\\')) {
- state = 3;
- } else {
- state = 0;
- }
-
- switch (state) {
- case 2: /* End of command block found */
- buffer[count - 1] = '\0';
- return (inputPtr);
-
- case 4: /* Escaped block designator */
- buffer[count] = c;
- state = 0;
- break;
-
- default: /* Add to command buffer */
- buffer[count++] = c;
- break;
- }
- if (count == HUGE_COMMAND_SIZE) {
- textPtr->interp->result = "command block is too long";
- return NULL;
- }
- }
- textPtr->interp->result = "premature end of TCL command block";
- return NULL;
- }
-
- static int
- IncludeText(interp, textPtr, fileName)
- Tcl_Interp *interp;
- Htext *textPtr;
- char *fileName;
- {
- char *inputPtr;
- int result;
-
- if ((textPtr->text == NULL) && (fileName == NULL)) {
- return TCL_OK; /* Empty text string */
- }
- inputPtr = textPtr->text;
- if (fileName != NULL) {
- inputPtr = ReadFile(interp, fileName);
- if (inputPtr == NULL) {
- return TCL_ERROR;
- }
- }
- result = ParseInput(interp, textPtr, inputPtr);
- if (fileName != NULL) {
- free(inputPtr);
- }
- return (result);
- }
-
- #ifdef STk_CODE
- static void CreateTraces(textPtr)
- Htext *textPtr;
- {
- }
-
- static void DeleteTraces(textPtr)
- Htext *textPtr;
- {
- }
-
- #else
- /* ARGSUSED */
- static char *
- HtextVarProc(clientData, interp, name1, name2, flags)
- ClientData clientData; /* Information about widget. */
- Tcl_Interp *interp; /* Interpreter containing variable. */
- char *name1; /* Name of variable. */
- char *name2; /* Second part of variable name. */
- int flags; /* Information about what happened. */
- {
- Htext *textPtr = (Htext *)clientData;
- Htext *lastTextPtr;
-
- /* Check to see of this is the most recent trace */
- lastTextPtr = (Htext *)Tcl_VarTraceInfo2(interp, name1, name2, flags,
- HtextVarProc, (ClientData)NULL);
- if (lastTextPtr != textPtr) {
- return NULL; /* Ignore all but most current trace */
- }
- if (flags & TCL_TRACE_READS) {
- char c;
-
- c = name2[0];
- if ((c == 'w') && (strcmp(name2, "widget") == 0)) {
- Tcl_SetVar2(interp, name1, name2, Tk_PathName(textPtr->tkwin),
- flags);
- } else if ((c == 'l') && (strcmp(name2, "line") == 0)) {
- char buf[80];
-
- sprintf(buf, "%d", textPtr->numLines);
- Tcl_SetVar2(interp, name1, name2, buf, flags);
- } else if ((c == 'f') && (strcmp(name2, "file") == 0)) {
- register char *fileName;
-
- fileName = textPtr->fileName;
- if (fileName == NULL) {
- fileName = "";
- }
- Tcl_SetVar2(interp, name1, name2, fileName, flags);
- } else {
- return ("Unknown variable");
- }
- }
- return NULL;
- }
-
- static void
- CreateTraces(textPtr)
- Htext *textPtr;
- {
- register Tcl_Interp *interp = textPtr->interp;
- int flags = (TCL_GLOBAL_ONLY | TCL_TRACE_READS);
- static char globalCmd[] = "global blt_htext";
-
- Tcl_TraceVar2(interp, HTEXT_CMDNAME, "widget", flags, HtextVarProc,
- (ClientData)textPtr);
- Tcl_TraceVar2(interp, HTEXT_CMDNAME, "line", flags, HtextVarProc,
- (ClientData)textPtr);
- Tcl_TraceVar2(interp, HTEXT_CMDNAME, "file", flags, HtextVarProc,
- (ClientData)textPtr);
- /*
- * Make the traced variables global to the widget
- */
- Tcl_Eval(interp, globalCmd);
- }
-
- static void
- DeleteTraces(textPtr)
- Htext *textPtr;
- {
- register Tcl_Interp *interp = textPtr->interp;
- int flags = (TCL_GLOBAL_ONLY | TCL_TRACE_READS);
-
- Tcl_UntraceVar2(interp, HTEXT_CMDNAME, "file", flags, HtextVarProc,
- (ClientData)textPtr);
- Tcl_UntraceVar2(interp, HTEXT_CMDNAME, "line", flags, HtextVarProc,
- (ClientData)textPtr);
- Tcl_UntraceVar2(interp, HTEXT_CMDNAME, "widget", flags, HtextVarProc,
- (ClientData)textPtr);
-
- }
- #endif
- /*
- * ----------------------------------------------------------------------
- *
- * FindChild --
- *
- * Searches for a child widget matching the path name given
- * If found, the pointer to the child structure is returned,
- * otherwise NULL.
- *
- * Results:
- * The pointer to the child structure. If not found, NULL.
- *
- * ----------------------------------------------------------------------
- */
- static Child *
- FindChild(textPtr, name)
- Htext *textPtr; /* Hypertext widget structure */
- char *name; /* Path name of child window */
- {
- Tcl_HashEntry *entryPtr;
- Tk_Window tkwin;
-
- tkwin = Tk_NameToWindow(textPtr->interp, name, textPtr->tkwin);
- if (tkwin == NULL) {
- return NULL;
- }
- entryPtr = Tcl_FindHashEntry(&(textPtr->subwindows), (char *)tkwin);
- if (entryPtr != NULL) {
- return (Child *) Tcl_GetHashValue(entryPtr);
- }
- return NULL;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * ChildGeometryProc --
- *
- * This procedure is invoked by Tk_GeometryRequest for
- * subwindows managed by the hypertext widget.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Arranges for tkwin, and all its managed siblings, to
- * be repacked and drawn at the next idle point.
- *
- *--------------------------------------------------------------
- */
- /* ARGSUSED */
- static void
- ChildGeometryProc(clientData, tkwin)
- ClientData clientData; /* Information about window that got new
- * preferred geometry. */
- Tk_Window tkwin; /* Other Tk-related information about the
- * window. */
- {
- Child *childPtr = (Child *)clientData;
-
- if ((childPtr->windowWidth != GetReqWidth(childPtr)) ||
- (childPtr->windowHeight != GetReqHeight(childPtr))) {
- childPtr->textPtr->flags |= REQUEST_LAYOUT;
- EventuallyRedraw(childPtr->textPtr);
- }
- }
-
- static void
- SendBogusEvent(tkwin)
- Tk_Window tkwin;
- {
- enum {
- DontPropagate = 0
- };
- XEvent event;
-
- event.type = event.xexpose.type = Expose;
- event.xexpose.window = Tk_WindowId(tkwin);
- event.xexpose.display = Tk_Display(tkwin);
- event.xexpose.count = 0;
- event.xexpose.x = event.xexpose.y = 0;
- event.xexpose.width = Tk_Width(tkwin);
- event.xexpose.height = Tk_Height(tkwin);
- XSendEvent(Tk_Display(tkwin), Tk_WindowId(tkwin), DontPropagate,
- ExposureMask, &event);
- }
-
- /*
- * --------------------------------------------------------------------
- *
- * ResizeArray --
- *
- * Reallocates memory to the new size given. New memory
- * is also cleared (zeros).
- *
- * Results:
- * Returns a pointer to the new object or NULL if an error occurred.
- *
- * Side Effects:
- * Memory is re/allocated.
- *
- * --------------------------------------------------------------------
- */
- static int
- ResizeArray(arrayPtr, elemSize, newLength, prevLength)
- char **arrayPtr;
- unsigned int elemSize;
- unsigned int newLength;
- unsigned int prevLength;
- {
- char *newPtr;
-
- if (newLength == prevLength) {
- return TCL_OK;
- }
- if (newLength == 0) { /* Free entire array */
- free(*arrayPtr);
- *arrayPtr = NULL;
- return TCL_OK;
- }
- newPtr = (char *)calloc(elemSize, newLength);
- if (newPtr == NULL) {
- return TCL_ERROR;
- }
- if ((prevLength > 0) && (*arrayPtr != NULL)) {
- unsigned int size;
-
- size = BLT_MIN(prevLength, newLength) * elemSize;
- if (size > 0) {
- memcpy(newPtr, *arrayPtr, size);
- }
- free(*arrayPtr);
- }
- *arrayPtr = newPtr;
- return TCL_OK;
- }
-
- #ifdef notdef
- static int
- TextXYToIndex(TextPtr, x, y)
- Text *TextPtr;
- int x, y;
-
- {
- Line *line = TextPtr->topLine;
- int result, dummy;
-
- /* locate the line */
-
- y -= TextPtr->borderWidth + 1;
-
- if (y > 0) {
- int count = y / TextPtr->pmHeight;
-
- while (line->length > 0 && count--)
- line = line->next;
- }
- if (line->length == 0)
- result = TextPtr->numChars;
- else
- result = line->index +
- TkMeasureChars(TextPtr->fontPtr,
- line->text,
- line->length,
- TextPtr->offset,
- x + TextPtr->leftIndex * TextPtr->avgWidth,
- 0, 0 , &dummy);
-
- return result;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * GetTextIndex --
- *
- * Parse an index into a Text widget and return either its value
- * or an error.
- *
- * Results:
- * A standard Tcl result. If all went well, then *indexPtr is
- * filled in with the index (into TextPtr) corresponding to
- * string. Otherwise an error message is left in interp->result.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- static int
- GetTextIndex(interp, TextPtr, string, indexPtr)
- Tcl_Interp *interp; /* For error messages. */
- Text *TextPtr; /* Text for which the index is being
- * specified. */
- char *string; /* Numerical index into TextPtr's element
- * list, or "end" to refer to last element. */
- int *indexPtr; /* Where to store converted relief. */
- {
- int length;
- char c;
-
- length = strlen(string);
- c = string[0];
-
- if ((c == 'e') && (strncmp(string, "end", length) == 0)) {
- *indexPtr = TextPtr->numChars;
- } else if ((c == 'c') && (strncmp(string, "cursor", length) == 0)) {
- *indexPtr = TextPtr->cursorPos;
- } else if ((c == 's') && (length > 4) &&
- (strncmp(string, "sel.first", length) == 0)) {
-
- if (TextPtr->selectFirst == -1) {
- interp->result = "selection isn't in Text";
- return TCL_ERROR;
- }
- } else if ((c == 's') && (length > 4) &&
- (strncmp(string, "sel.last", length) == 0)) {
- *indexPtr = TextPtr->selectLast;
- } else if ((c == 't') && (strncmp(string, "top", length) == 0)) {
- *indexPtr = TextPtr->topLine->index;
- } else if (c == '@') {
- char *comma;
- int x, y;
-
- string++;
- if ((comma = strchr(string, ',')) == NULL) {
- goto badIndex;
- }
- *comma = '\0';
- if ((Tcl_GetInt(interp, string, &x) != TCL_OK) ||
- (Tcl_GetInt(interp, comma + 1, &y) != TCL_OK)) {
- goto badIndex;
- }
- if (TextPtr->numChars == 0) {
- *indexPtr = 0;
- } else {
- *indexPtr = TextXYToIndex(TextPtr, x, y);
- }
- } else {
- if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
- goto badIndex;
- }
- if (*indexPtr < 0) {
- *indexPtr = 0;
- } else if (*indexPtr > TextPtr->numChars) {
- *indexPtr = TextPtr->numChars;
- }
- }
- return TCL_OK;
- badIndex:
-
- /*
- * Some of the paths here leave messages in interp->result, so we
- * have to clear it out before storing our own message.
- */
- Tcl_SetResult(interp, (char *)NULL, TCL_STATIC);
- Tcl_AppendResult(interp, "bad text index \"", string,
- "\"", (char *)NULL);
- return TCL_ERROR;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TextSelectTo --
- *
- * Modify the selection by moving its un-anchored end. This could
- * make the selection either larger or smaller.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The selection changes.
- *
- *----------------------------------------------------------------------
- */
- static void
- TextSelectTo(TextPtr, index)
- register Text *TextPtr; /* Information about widget. */
- int index; /* Index of element that is to
- * become the "other" end of the
- * selection. */
- {
- int newFirst, newLast;
-
- /*
- * Grab the selection if we don't own it already.
- */
-
- if (TextPtr->selectFirst == -1) {
- Tk_OwnSelection(TextPtr->tkwin, TextLostSelection,
- (ClientData)TextPtr);
- }
- if (index < 0) {
- index = 0;
- }
- if (index >= TextPtr->numChars) {
- index = TextPtr->numChars - 1;
- }
- if (TextPtr->selectAnchor > TextPtr->numChars) {
- TextPtr->selectAnchor = TextPtr->numChars;
- }
- if (TextPtr->selectAnchor <= index) {
- newFirst = TextPtr->selectAnchor;
- newLast = index;
- } else {
- newFirst = index;
- newLast = TextPtr->selectAnchor - 1;
- if (newLast < 0) {
- newFirst = newLast = -1;
- }
- }
- if ((TextPtr->selectFirst == newFirst)
- && (TextPtr->selectLast == newLast)) {
- return;
- }
- TextPtr->selectFirst = newFirst;
- TextPtr->selectLast = newLast;
- TextPtr->flags |= REFRESH_NEEDED;
- EventuallyRedraw(TextPtr);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TextFetchSelection --
- *
- * This procedure is called back by Tk when the selection is
- * requested by someone. It returns part or all of the selection
- * in a buffer provided by the caller.
- *
- * Results:
- * The return value is the number of non-NULL bytes stored
- * at buffer. Buffer is filled (or partially filled) with a
- * NULL-terminated string containing part or all of the selection,
- * as given by offset and maxBytes.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static int
- TextFetchSelection(clientData, offset, buffer, maxBytes)
- ClientData clientData; /* Information about Text widget. */
- int offset; /* Offset within selection of first
- * character to be returned. */
- char *buffer; /* Location in which to place
- * selection. */
- int maxBytes; /* Maximum number of bytes to place
- * at buffer, not including terminating
- * NULL character. */
- {
- Text *TextPtr = (Text *) clientData;
- char *text;
- int count;
-
- if (TextPtr->selectFirst < 0) {
- return -1;
- }
- count = TextPtr->selectLast + 1 - TextPtr->selectFirst - offset;
- if (count > maxBytes) {
- count = maxBytes;
- }
- if (count <= 0) {
- return 0;
- }
- text = GetText(TextPtr, TextPtr->selectFirst + offset, count);
-
- if (text != NULL) {
- strcpy(buffer, text);
- free(text);
- } else
- count = 0;
-
- return count;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TextLostSelection --
- *
- * This procedure is called back by Tk when the selection is
- * grabbed away from a Text widget.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The existing selection is unhighlighted, and the window is
- * marked as not containing a selection.
- *
- *----------------------------------------------------------------------
- */
- static void
- TextLostSelection(clientData)
- ClientData clientData; /* Information about Text widget. */
- {
- Text *TextPtr = (Text *) clientData;
-
- TextPtr->selectFirst = -1;
- TextPtr->selectLast = -1;
- EventuallyRedraw(TextPtr);
- }
-
- #endif
-
- /*
- *----------------------------------------------------------------------
- *
- * GotoLine --
- *
- * Move the top line of the viewport to the new location based
- * upon the given line number. Force outlier requests to the
- * top or bottom of text.
- *
- * Results:
- * Stanard Tcl result. If TCL_OK, interp->result contains the
- * current line number.
- *
- * Side effects:
- * At the next idle point, the text viewport will be move to the
- * new line.
- *
- *----------------------------------------------------------------------
- */
- static int
- GotoLine(textPtr, interp, argc, argv)
- Htext *textPtr;
- Tcl_Interp *interp;
- int argc;
- char **argv;
- {
- int line;
-
- if (argc > 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " gotoline ?line?\"", (char *)NULL);
- return TCL_ERROR;
- }
- line = textPtr->first;
- if (argc == 3) {
- if ((argv[2][0] == 'e') && (strcmp(argv[2], "end") == 0)) {
- line = textPtr->numLines - 1;
- } else {
- if (Tcl_GetInt(textPtr->interp, argv[2], &line) != TCL_OK) {
- return TCL_ERROR;
- }
- /* Keep the line request within the current range of lines */
- if (line < 1) {
- line = 1;
- } else if (line > textPtr->numLines) {
- line = textPtr->numLines;
- }
- line--;
- }
- textPtr->reqLineNum = line;
- textPtr->flags |= VIEW_MOVED;
-
- /*
- * Make only a request for a change in the viewport. Defer
- * the actual scrolling until the text layout is adjusted at
- * the next idle point.
- */
- if (line != textPtr->first) {
- textPtr->flags |= REQUEST_GOTO;
- EventuallyRedraw(textPtr);
- }
- }
- sprintf(textPtr->interp->result, "%d", line + 1);
- return TCL_OK;
- }
-
- static int
- ScrollX(textPtr, string)
- Htext *textPtr;
- char *string;
- {
- int x;
-
- x = textPtr->vPort.x;
- if (string != NULL) {
- if (Tk_GetPixels(textPtr->interp,
- textPtr->tkwin, string, &x) != TCL_OK) {
- return TCL_ERROR;
- }
- x *= textPtr->scrollX; /* Convert to pixels */
- if (x > textPtr->vPort.width) {
- x = textPtr->vPort.width - 1;
- } else if (x < 0) {
- x = 0;
- }
- if (x != textPtr->vPort.x) {
- textPtr->pendingX = x;
- textPtr->flags |= VIEW_MOVED;
- EventuallyRedraw(textPtr);
- }
- }
- sprintf(textPtr->interp->result, "%d", x / textPtr->scrollX);
- return TCL_OK;
- }
-
-
- static int
- ScrollY(textPtr, string)
- Htext *textPtr;
- char *string;
- {
- int y;
-
- y = textPtr->vPort.y;
- if (string != NULL) {
- if (Tk_GetPixels(textPtr->interp,
- textPtr->tkwin, string, &y) != TCL_OK) {
- return TCL_ERROR;
- }
- y *= textPtr->scrollY; /* Convert to pixels */
- if (y > textPtr->vPort.height) {
- y = textPtr->vPort.height - 1;
- } else if (y < 0) {
- y = 0;
- }
- if (y != textPtr->vPort.y) {
- textPtr->pendingY = y;
- textPtr->flags |= VIEW_MOVED;
- EventuallyRedraw(textPtr);
- }
- }
- sprintf(textPtr->interp->result, "%d", y / textPtr->scrollY);
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetChildWindows --
- *
- * Returns a list of all the pathNames of child windows of the
- * Htext widget. If a pattern argument is given, only the names
- * of windows matching it will be placed into the list.
- *
- * Results:
- * Standard Tcl result. If TCL_OK, interp->result will contain
- * the list of the child window pathnames. Otherwise it will
- * contain an error message.
- *
- *----------------------------------------------------------------------
- */
- static int
- GetChildren(textPtr, interp, argc, argv)
- Htext *textPtr; /* Hypertext widget record */
- Tcl_Interp *interp; /* Interpreter associated with widget */
- int argc;
- char **argv;
- {
- Child *childPtr;
- Tcl_HashEntry *entryPtr;
- Tcl_HashSearch searchId;
- char *name;
-
- if ((argc != 2) && (argc != 3)) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " children ?pattern?\"", (char *)NULL);
- return TCL_ERROR;
- }
- for (entryPtr = Tcl_FirstHashEntry(&(textPtr->subwindows), &searchId);
- entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&searchId)) {
- childPtr = (Child *)Tcl_GetHashValue(entryPtr);
- if (childPtr->tkwin == NULL) {
- fprintf(stderr, "window `%s' is null\n",
- Tk_PathName(Tcl_GetHashKey(&(textPtr->subwindows), entryPtr)));
- continue;
- }
- name = Tk_PathName(childPtr->tkwin);
- if ((argc == 2) || (Tcl_StringMatch(name, argv[2]))) {
- Tcl_AppendElement(interp, name);
- }
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ConfigureParent --
- *
- * This procedure is called to process an argv/argc list, plus
- * the Tk option database, in order to configure (or reconfigure)
- * a hypertext widget.
- *
- * Results:
- * The return value is a standard Tcl result. If TCL_ERROR is
- * returned, then interp->result contains an error message.
- *
- * Side effects:
- * Configuration information, such as text string, colors, font,
- * etc. get set for textPtr; old resources get freed, if there were any.
- * The hypertext is redisplayed.
- *
- *----------------------------------------------------------------------
- */
- static int
- ConfigureParent(textPtr, interp, argc, argv)
- Htext *textPtr;
- Tcl_Interp *interp;
- int argc;
- char **argv;
- {
- if (argc == 2) {
- return (Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
- (char *)textPtr, (char *)NULL, 0));
- } else if (argc == 3) {
- return (Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
- (char *)textPtr, argv[2], 0));
- } else {
- if (ConfigureHtext(interp, textPtr, argc - 2, argv + 2,
- TK_CONFIG_ARGV_ONLY) != TCL_OK) {
- return TCL_ERROR;
- }
- EventuallyRedraw(textPtr);
- }
- return TCL_OK;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * ConfigureChild --
- *
- * This procedure is called to process an argv/argc list, plus
- * the Tk option database, in order to configure (or reconfigure)
- * a hypertext child.
- *
- * Results:
- * The return value is a standard Tcl result. If TCL_ERROR is
- * returned, then interp->result contains an error message.
- *
- * Side effects:
- * Configuration information get set for the child cavity; old
- * resources get freed, if there were any. The child is marked
- * for redisplay.
- *
- *----------------------------------------------------------------------
- */
- static int
- ConfigureChild(textPtr, interp, argc, argv)
- Htext *textPtr;
- Tcl_Interp *interp;
- int argc;
- char **argv;
- {
- Child *childPtr;
-
- if (argc < 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " childconfigure childName ?args...?\"", (char *)NULL);
- return TCL_ERROR;
- }
- childPtr = FindChild(textPtr, argv[2]);
- if (childPtr == NULL) {
- Tcl_AppendResult(interp, "can't find window \"", argv[2], "\" in \"",
- Tk_PathName(textPtr->tkwin), "\"", (char *)NULL);
- return TCL_ERROR;
- }
- if (argc == 3) {
- return (Tk_ConfigureInfo(interp, textPtr->tkwin, childConfigSpecs,
- (char *)childPtr, (char *)NULL, 0));
- } else if (argc == 4) {
- return (Tk_ConfigureInfo(interp, textPtr->tkwin, childConfigSpecs,
- (char *)childPtr, argv[3], 0));
- } else {
- if (Tk_ConfigureWidget(interp, textPtr->tkwin, childConfigSpecs,
- argc - 3, argv + 3, (char *)childPtr,
- TK_CONFIG_ARGV_ONLY) != TCL_OK) {
- return TCL_ERROR;
- }
- textPtr->flags |= REQUEST_LAYOUT;
- EventuallyRedraw(textPtr);
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ScanText --
- *
- * Implements the quick scan for hypertext widgets.
- *
- *----------------------------------------------------------------------
- */
- static int
- ScanText(textPtr, interp, argc, argv)
- Htext *textPtr;
- Tcl_Interp *interp;
- int argc;
- char **argv;
- {
- int markX, markY;
- char c;
- int length;
-
- if (argc != 5) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " scan mark|dragto x y\"", (char *)NULL);
- return TCL_ERROR;
- }
- if ((Tcl_GetInt(interp, argv[3], &markX) != TCL_OK) ||
- (Tcl_GetInt(interp, argv[4], &markY) != TCL_OK)) {
- return TCL_ERROR;
- }
- c = argv[2][0];
- length = strlen(argv[2]);
- if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
- textPtr->scanMark.x = markX, textPtr->scanMark.y = markY;
- textPtr->scanPt.x = textPtr->vPort.x;
- textPtr->scanPt.y = textPtr->vPort.y;
-
- } else if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
- int x, y;
-
- x = textPtr->scanPt.x - (10 * (markX - textPtr->scanMark.x));
- y = textPtr->scanPt.y - (10 * (markY - textPtr->scanMark.y));
-
- if (x < 0) {
- x = textPtr->scanPt.x = 0;
- textPtr->scanMark.x = markX;
- } else if (x >= textPtr->vPort.width) {
- x = textPtr->scanPt.x = textPtr->vPort.width - textPtr->scrollX;
- textPtr->scanMark.x = markX;
- }
- if (y < 0) {
- y = textPtr->scanPt.y = 0;
- textPtr->scanMark.y = markY;
- } else if (y >= textPtr->vPort.height) {
- y = textPtr->scanPt.y = textPtr->vPort.height - textPtr->scrollY;
- textPtr->scanMark.y = markY;
- }
- if ((y != textPtr->pendingY) || (x != textPtr->pendingX)) {
- textPtr->pendingX = x, textPtr->pendingY = y;
- textPtr->flags |= VIEW_MOVED;
- EventuallyRedraw(textPtr);
- }
- } else {
- Tcl_AppendResult(interp, "bad scan option \"", argv[2],
- "\": must be mark or dragto", (char *)NULL);
- return TCL_ERROR;
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * SearchText --
- *
- * Returns the linenumber of the next line matching the given
- * pattern within the range of lines provided. If the first
- * line number is greater than the last, the search is done in
- * reverse.
- *
- *----------------------------------------------------------------------
- */
- static int
- SearchText(textPtr, interp, argc, argv)
- Htext *textPtr;
- Tcl_Interp *interp;
- int argc;
- char **argv;
- {
- int first, last;
- register int i;
- register char *string;
- int lineNum;
- enum Direction {
- REVERSE = -1, FORWARD = 1
- } direction; /* Indicates the direction of the search */
-
- if ((argc < 3) || (argc > 5)) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " search pattern ?from? ?to?\"", (char *)NULL);
- return TCL_ERROR;
- }
- first = 0;
- last = textPtr->numLines - 1;
-
- if (argc > 3) {
- if (argv[3][0] == 'e' && strcmp(argv[3], "end") == 0) {
- first = textPtr->numLines - 1;
- } else {
- if (Tcl_GetInt(interp, argv[3], &first) != TCL_OK) {
- return TCL_ERROR;
- }
- first--;
- if (first < 0) {
- first = 0;
- } else if (first >= textPtr->numLines) {
- first = textPtr->numLines - 1;
- }
- }
- }
- if (argc > 4) {
- if ((argv[4][0] == 'e') && (strcmp(argv[4], "end") == 0)) {
- last = textPtr->numLines - 1;
- } else {
- if (Tcl_GetInt(interp, argv[4], &last) != TCL_OK) {
- return TCL_ERROR;
- }
- last--;
- if (last < 0) {
- last = 0;
- } else if (last >= textPtr->numLines) {
- last = textPtr->numLines - 1;
- }
- }
- }
- direction = (first > last) ? REVERSE : FORWARD;
- lineNum = -1;
- for (i = first; i != (last + direction); i += direction) {
- string = textPtr->linePtrPtr[i]->text;
- if ((string != NULL) && Tcl_StringMatch(string, argv[2])) {
- lineNum = i + 1;
- break;
- }
- }
- sprintf(textPtr->interp->result, "%d", lineNum);
- return TCL_OK;
- }
-
- /*
- * --------------------------------------------------------------
- *
- * TextWidgetCmd --
- *
- * This procedure is invoked to process the Tcl command that
- * corresponds to a widget managed by this module. See the user
- * documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- * --------------------------------------------------------------
- */
- static int
- TextWidgetCmd(clientData, interp, argc, argv)
- ClientData clientData; /* Information about hypertext widget. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- register Htext *textPtr = (Htext *)clientData;
- int result = TCL_OK;
- int length;
- char c;
-
- if (argc < 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " option ?arg arg ...?\"", (char *)NULL);
- return TCL_ERROR;
- }
- Tk_Preserve((ClientData)textPtr);
- c = argv[1][0];
- length = strlen(argv[1]);
-
- if ((c == 'a') && (strncmp(argv[1], "append", length) == 0)) {
- result = AppendChild(textPtr, interp, argc, argv);
- } else if ((c == 'c') && (length > 5) &&
- (strncmp(argv[1], "childconfigure", length) == 0)) {
- result = ConfigureChild(textPtr, interp, argc, argv);
- } else if ((c == 'c') && (length > 5) &&
- (strncmp(argv[1], "children", length) == 0)) {
- result = GetChildren(textPtr, interp, argc, argv);
- } else if ((c == 'c') && (length > 1) &&
- (strncmp(argv[1], "configure", length) == 0)) {
- result = ConfigureParent(textPtr, interp, argc, argv);
- } else if ((c == 'c') && (length > 1) &&
- (strncmp(argv[1], "cget", length) == 0)) {
- if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " cget option\"", (char *) NULL);
- goto error;
- }
- result = Tk_ConfigureValue(interp, textPtr->tkwin, configSpecs,
- (char *) textPtr, argv[2], 0);
- } else if ((c == 'g') && (strncmp(argv[1], "gotoline", length) == 0)) {
- result = GotoLine(textPtr, interp, argc, argv);
- } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
- if (argc > 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " xview ?position?\"", (char *)NULL);
- goto error;
- }
- result = ScrollX(textPtr, argv[2]);
- } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
- if (argc > 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " yview ?position?\"", (char *)NULL);
- goto error;
- }
- result = ScrollY(textPtr, argv[2]);
- } else if ((c == 's') && (length > 1)
- && (strncmp(argv[1], "scan", length) == 0)) {
- result = ScanText(textPtr, interp, argc, argv);
- } else if ((c == 's') && (length > 2) &&
- strncmp(argv[1], "search", length) == 0) {
- result = SearchText(textPtr, interp, argc, argv);
- #ifdef notdef
- } else if ((c == 's') && (length > 1) &&
- strncmp(argv[1], "select", length) == 0) {
- int index;
-
- if (argc < 3) {
- Tcl_AppendResult(interp, "too few args: should be \"",
- argv[0], " select option ?index?\"", (char *)NULL);
- goto error;
- }
- length = strlen(argv[2]);
- c = argv[2][0];
- if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
- if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " select clear\"", (char *)NULL);
- goto error;
- }
- if (textPtr->selectFirst != -1) {
- textPtr->selectFirst = textPtr->selectLast = -1;
- goto redisplay;
- }
- goto error;
- }
- if (argc >= 4) {
- if (GetTextIndex(interp, textPtr, argv[3], &index) != TCL_OK) {
- goto error;
- }
- }
- if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " select adjust index\"",
- (char *)NULL);
- goto error;
- }
- TextSelectTo(textPtr, index);
- } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " select from index\"",
- (char *)NULL);
- goto error;
- }
- textPtr->selectAnchor = index;
- } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " select to index\"", (char *)NULL);
- goto error;
- }
- TextSelectTo(textPtr, index);
- } else {
- Tcl_AppendResult(interp, "bad select option \"", argv[2],
- "\": must be adjust, clear, from, or to", (char *)NULL);
- goto error;
- }
- #endif
- } else {
- Tcl_AppendResult(interp, "bad option \"", argv[1], "\": should be ",
- "append, configure, childconfigure, gotoline, scan, search, xview, or yview",
- (char *)NULL);
- }
- error:
- Tk_Release((ClientData)textPtr);
- return result;
- }
-
- /*
- * --------------------------------------------------------------
- *
- * HtextCmd --
- *
- * This procedure is invoked to process the "htext" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- * --------------------------------------------------------------
- */
-
- static int
- HtextCmd(clientData, interp, argc, argv)
- ClientData clientData; /* Main window associated with interpreter. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- register Htext *textPtr;
- Tk_Window mainWindow = (Tk_Window)clientData;
- Tk_Window tkwin;
-
- if (argc < 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " pathName ?options?\"", (char *)NULL);
- return TCL_ERROR;
- }
- textPtr = (Htext *)calloc(1, sizeof(Htext));
- if (textPtr == NULL) {
- interp->result = "can't allocate text structure";
- return TCL_ERROR;
- }
- tkwin = Tk_CreateWindowFromPath(interp, mainWindow, argv[1], (char *)NULL);
- if (tkwin == NULL) {
- free(textPtr);
- return TCL_ERROR;
- }
- /* Initialize the new hypertext widget */
-
- Tk_SetClass(tkwin, "Blt_htext");
- textPtr->tkwin = tkwin;
- textPtr->display = Tk_Display(tkwin);
- textPtr->interp = interp;
- textPtr->numLines = textPtr->arraySize = 0;
- textPtr->lineSpacing = 1;
- textPtr->scrollX = textPtr->scrollY = 10;
- textPtr->relief = TK_RELIEF_FLAT;
- Tcl_InitHashTable(&(textPtr->subwindows), TCL_ONE_WORD_KEYS);
-
- /*
- * -----------------------------------------------------------------
- *
- * Create the widget command before configuring the widget. This
- * is because the "-file" and "-text" options may have embedded
- * commands that self-reference the widget through the
- * "$blt_htext(widget)" variable.
- *
- * ------------------------------------------------------------------
- */
-
- Tcl_CreateCommand(interp, argv[1], TextWidgetCmd, (ClientData)textPtr,
- (Tcl_CmdDeleteProc *)NULL);
- Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
- TextEventProc, (ClientData)textPtr);
-
- if (ConfigureHtext(interp, textPtr, argc - 2, argv + 2, 0) != TCL_OK) {
- Tk_DestroyWindow(textPtr->tkwin);
- return TCL_ERROR;
- }
- Tcl_SetResult(interp, Tk_PathName(textPtr->tkwin), TCL_STATIC);
-
- return TCL_OK;
- }
-
- int
- Blt_HtextInit(interp)
- Tcl_Interp *interp;
- {
- Tk_Window tkwin;
-
- if (Blt_FindCmd(interp, HTEXT_CMDNAME, (ClientData *)NULL) == TCL_OK) {
- Tcl_AppendResult(interp, "\"blt_htext\" command already exists",
- (char *)NULL);
- return TCL_ERROR;
- }
- tkwin = Tk_MainWindow(interp);
- if (tkwin == NULL) {
- Tcl_AppendResult(interp, "\"blt_htext\" requires Tk", (char *)NULL);
- return TCL_ERROR;
- }
- Tcl_SetVar2(interp, "blt_versions", HTEXT_CMDNAME, HTEXT_VERSION,
- TCL_GLOBAL_ONLY);
- Tcl_CreateCommand(interp, HTEXT_CMDNAME, HtextCmd, (ClientData)tkwin,
- (Tcl_CmdDeleteProc *)NULL);
- return TCL_OK;
- }
-